mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
This commit is contained in:
commit
7f46b1343f
2400 changed files with 559938 additions and 49536 deletions
|
|
@ -254,6 +254,8 @@ source "net/mac80211/Kconfig"
|
|||
|
||||
endif # WIRELESS
|
||||
|
||||
source "net/wimax/Kconfig"
|
||||
|
||||
source "net/rfkill/Kconfig"
|
||||
source "net/9p/Kconfig"
|
||||
|
||||
|
|
|
|||
|
|
@ -63,3 +63,4 @@ endif
|
|||
ifeq ($(CONFIG_NET),y)
|
||||
obj-$(CONFIG_SYSCTL) += sysctl_net.o
|
||||
endif
|
||||
obj-$(CONFIG_WIMAX) += wimax/
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/audit.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/tcp.h>
|
||||
|
|
@ -449,6 +450,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
|
|||
/**
|
||||
* cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
|
||||
* @doi_def: the DOI structure
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* The caller defines a new DOI for use by the CIPSO engine and calls this
|
||||
|
|
@ -458,50 +460,78 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
|
|||
* zero on success and non-zero on failure.
|
||||
*
|
||||
*/
|
||||
int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
|
||||
int cipso_v4_doi_add(struct cipso_v4_doi *doi_def,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
u32 iter;
|
||||
u32 doi;
|
||||
u32 doi_type;
|
||||
struct audit_buffer *audit_buf;
|
||||
|
||||
doi = doi_def->doi;
|
||||
doi_type = doi_def->type;
|
||||
|
||||
if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN)
|
||||
return -EINVAL;
|
||||
goto doi_add_return;
|
||||
for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
|
||||
switch (doi_def->tags[iter]) {
|
||||
case CIPSO_V4_TAG_RBITMAP:
|
||||
break;
|
||||
case CIPSO_V4_TAG_RANGE:
|
||||
if (doi_def->type != CIPSO_V4_MAP_PASS)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case CIPSO_V4_TAG_INVALID:
|
||||
if (iter == 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case CIPSO_V4_TAG_ENUM:
|
||||
if (doi_def->type != CIPSO_V4_MAP_PASS)
|
||||
return -EINVAL;
|
||||
goto doi_add_return;
|
||||
break;
|
||||
case CIPSO_V4_TAG_LOCAL:
|
||||
if (doi_def->type != CIPSO_V4_MAP_LOCAL)
|
||||
return -EINVAL;
|
||||
goto doi_add_return;
|
||||
break;
|
||||
case CIPSO_V4_TAG_INVALID:
|
||||
if (iter == 0)
|
||||
goto doi_add_return;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
goto doi_add_return;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_set(&doi_def->refcount, 1);
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
if (cipso_v4_doi_search(doi_def->doi) != NULL)
|
||||
goto doi_add_failure;
|
||||
if (cipso_v4_doi_search(doi_def->doi) != NULL) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
ret_val = -EEXIST;
|
||||
goto doi_add_return;
|
||||
}
|
||||
list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
ret_val = 0;
|
||||
|
||||
return 0;
|
||||
doi_add_return:
|
||||
audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
const char *type_str;
|
||||
switch (doi_type) {
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
type_str = "trans";
|
||||
break;
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
type_str = "pass";
|
||||
break;
|
||||
case CIPSO_V4_MAP_LOCAL:
|
||||
type_str = "local";
|
||||
break;
|
||||
default:
|
||||
type_str = "(unknown)";
|
||||
}
|
||||
audit_log_format(audit_buf,
|
||||
" cipso_doi=%u cipso_type=%s res=%u",
|
||||
doi, type_str, ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
doi_add_failure:
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
return -EEXIST;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -559,25 +589,39 @@ static void cipso_v4_doi_free_rcu(struct rcu_head *entry)
|
|||
*/
|
||||
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val;
|
||||
struct cipso_v4_doi *doi_def;
|
||||
struct audit_buffer *audit_buf;
|
||||
|
||||
spin_lock(&cipso_v4_doi_list_lock);
|
||||
doi_def = cipso_v4_doi_search(doi);
|
||||
if (doi_def == NULL) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
return -ENOENT;
|
||||
ret_val = -ENOENT;
|
||||
goto doi_remove_return;
|
||||
}
|
||||
if (!atomic_dec_and_test(&doi_def->refcount)) {
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
return -EBUSY;
|
||||
ret_val = -EBUSY;
|
||||
goto doi_remove_return;
|
||||
}
|
||||
list_del_rcu(&doi_def->list);
|
||||
spin_unlock(&cipso_v4_doi_list_lock);
|
||||
|
||||
cipso_v4_cache_invalidate();
|
||||
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
|
||||
ret_val = 0;
|
||||
|
||||
return 0;
|
||||
doi_remove_return:
|
||||
audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
audit_log_format(audit_buf,
|
||||
" cipso_doi=%u res=%u",
|
||||
doi, ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
#include <asm/ebcdic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/s390_ext.h>
|
||||
#include <asm/s390_rdev.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
/*
|
||||
|
|
@ -1696,7 +1695,7 @@ static int __init iucv_init(void)
|
|||
rc = register_external_interrupt(0x4000, iucv_external_interrupt);
|
||||
if (rc)
|
||||
goto out;
|
||||
iucv_root = s390_root_dev_register("iucv");
|
||||
iucv_root = root_device_register("iucv");
|
||||
if (IS_ERR(iucv_root)) {
|
||||
rc = PTR_ERR(iucv_root);
|
||||
goto out_int;
|
||||
|
|
@ -1740,7 +1739,7 @@ out_free:
|
|||
kfree(iucv_irq_data[cpu]);
|
||||
iucv_irq_data[cpu] = NULL;
|
||||
}
|
||||
s390_root_dev_unregister(iucv_root);
|
||||
root_device_unregister(iucv_root);
|
||||
out_int:
|
||||
unregister_external_interrupt(0x4000, iucv_external_interrupt);
|
||||
out:
|
||||
|
|
@ -1770,7 +1769,7 @@ static void __exit iucv_exit(void)
|
|||
kfree(iucv_irq_data[cpu]);
|
||||
iucv_irq_data[cpu] = NULL;
|
||||
}
|
||||
s390_root_dev_unregister(iucv_root);
|
||||
root_device_unregister(iucv_root);
|
||||
bus_unregister(&iucv_bus);
|
||||
unregister_external_interrupt(0x4000, iucv_external_interrupt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
|
|||
/**
|
||||
* netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
|
||||
* @info: the Generic NETLINK info block
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
|
||||
|
|
@ -137,7 +138,8 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
|
|||
* non-zero on error.
|
||||
*
|
||||
*/
|
||||
static int netlbl_cipsov4_add_std(struct genl_info *info)
|
||||
static int netlbl_cipsov4_add_std(struct genl_info *info,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
struct cipso_v4_doi *doi_def = NULL;
|
||||
|
|
@ -316,7 +318,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
|
|||
}
|
||||
}
|
||||
|
||||
ret_val = cipso_v4_doi_add(doi_def);
|
||||
ret_val = cipso_v4_doi_add(doi_def, audit_info);
|
||||
if (ret_val != 0)
|
||||
goto add_std_failure;
|
||||
return 0;
|
||||
|
|
@ -330,6 +332,7 @@ add_std_failure:
|
|||
/**
|
||||
* netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
|
||||
* @info: the Generic NETLINK info block
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
|
||||
|
|
@ -337,7 +340,8 @@ add_std_failure:
|
|||
* error.
|
||||
*
|
||||
*/
|
||||
static int netlbl_cipsov4_add_pass(struct genl_info *info)
|
||||
static int netlbl_cipsov4_add_pass(struct genl_info *info,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val;
|
||||
struct cipso_v4_doi *doi_def = NULL;
|
||||
|
|
@ -354,7 +358,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
|
|||
if (ret_val != 0)
|
||||
goto add_pass_failure;
|
||||
|
||||
ret_val = cipso_v4_doi_add(doi_def);
|
||||
ret_val = cipso_v4_doi_add(doi_def, audit_info);
|
||||
if (ret_val != 0)
|
||||
goto add_pass_failure;
|
||||
return 0;
|
||||
|
|
@ -367,6 +371,7 @@ add_pass_failure:
|
|||
/**
|
||||
* netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
|
||||
* @info: the Generic NETLINK info block
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
|
||||
|
|
@ -374,7 +379,8 @@ add_pass_failure:
|
|||
* non-zero on error.
|
||||
*
|
||||
*/
|
||||
static int netlbl_cipsov4_add_local(struct genl_info *info)
|
||||
static int netlbl_cipsov4_add_local(struct genl_info *info,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val;
|
||||
struct cipso_v4_doi *doi_def = NULL;
|
||||
|
|
@ -391,7 +397,7 @@ static int netlbl_cipsov4_add_local(struct genl_info *info)
|
|||
if (ret_val != 0)
|
||||
goto add_local_failure;
|
||||
|
||||
ret_val = cipso_v4_doi_add(doi_def);
|
||||
ret_val = cipso_v4_doi_add(doi_def, audit_info);
|
||||
if (ret_val != 0)
|
||||
goto add_local_failure;
|
||||
return 0;
|
||||
|
|
@ -415,48 +421,31 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
u32 type;
|
||||
u32 doi;
|
||||
const char *type_str = "(unknown)";
|
||||
struct audit_buffer *audit_buf;
|
||||
struct netlbl_audit audit_info;
|
||||
|
||||
if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
|
||||
!info->attrs[NLBL_CIPSOV4_A_MTYPE])
|
||||
return -EINVAL;
|
||||
|
||||
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
|
||||
netlbl_netlink_auditinfo(skb, &audit_info);
|
||||
|
||||
type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
|
||||
switch (type) {
|
||||
switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
type_str = "trans";
|
||||
ret_val = netlbl_cipsov4_add_std(info);
|
||||
ret_val = netlbl_cipsov4_add_std(info, &audit_info);
|
||||
break;
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
type_str = "pass";
|
||||
ret_val = netlbl_cipsov4_add_pass(info);
|
||||
ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
|
||||
break;
|
||||
case CIPSO_V4_MAP_LOCAL:
|
||||
type_str = "local";
|
||||
ret_val = netlbl_cipsov4_add_local(info);
|
||||
ret_val = netlbl_cipsov4_add_local(info, &audit_info);
|
||||
break;
|
||||
}
|
||||
if (ret_val == 0)
|
||||
atomic_inc(&netlabel_mgmt_protocount);
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
|
||||
&audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
audit_log_format(audit_buf,
|
||||
" cipso_doi=%u cipso_type=%s res=%u",
|
||||
doi,
|
||||
type_str,
|
||||
ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
|
@ -725,9 +714,7 @@ static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
|
|||
static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int ret_val = -EINVAL;
|
||||
u32 doi = 0;
|
||||
struct netlbl_domhsh_walk_arg cb_arg;
|
||||
struct audit_buffer *audit_buf;
|
||||
struct netlbl_audit audit_info;
|
||||
u32 skip_bkt = 0;
|
||||
u32 skip_chain = 0;
|
||||
|
|
@ -735,29 +722,17 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
|
|||
if (!info->attrs[NLBL_CIPSOV4_A_DOI])
|
||||
return -EINVAL;
|
||||
|
||||
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
|
||||
netlbl_netlink_auditinfo(skb, &audit_info);
|
||||
|
||||
cb_arg.doi = doi;
|
||||
cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
|
||||
cb_arg.audit_info = &audit_info;
|
||||
ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
|
||||
netlbl_cipsov4_remove_cb, &cb_arg);
|
||||
if (ret_val == 0 || ret_val == -ENOENT) {
|
||||
ret_val = cipso_v4_doi_remove(doi, &audit_info);
|
||||
ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
|
||||
if (ret_val == 0)
|
||||
atomic_dec(&netlabel_mgmt_protocount);
|
||||
}
|
||||
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
|
||||
&audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
audit_log_format(audit_buf,
|
||||
" cipso_doi=%u res=%u",
|
||||
doi,
|
||||
ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -482,6 +482,73 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
|
|||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_domhsh_remove_af4 - Removes an address selector entry
|
||||
* @domain: the domain
|
||||
* @addr: IPv4 address
|
||||
* @mask: IPv4 address mask
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Removes an individual address selector from a domain mapping and potentially
|
||||
* the entire mapping if it is empty. Returns zero on success, negative values
|
||||
* on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_domhsh_remove_af4(const char *domain,
|
||||
const struct in_addr *addr,
|
||||
const struct in_addr *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
struct netlbl_dom_map *entry_map;
|
||||
struct netlbl_af4list *entry_addr;
|
||||
struct netlbl_af4list *iter4;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
struct netlbl_af6list *iter6;
|
||||
#endif /* IPv6 */
|
||||
struct netlbl_domaddr4_map *entry;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (domain)
|
||||
entry_map = netlbl_domhsh_search(domain);
|
||||
else
|
||||
entry_map = netlbl_domhsh_search_def(domain);
|
||||
if (entry_map == NULL || entry_map->type != NETLBL_NLTYPE_ADDRSELECT)
|
||||
goto remove_af4_failure;
|
||||
|
||||
spin_lock(&netlbl_domhsh_lock);
|
||||
entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
|
||||
&entry_map->type_def.addrsel->list4);
|
||||
spin_unlock(&netlbl_domhsh_lock);
|
||||
|
||||
if (entry_addr == NULL)
|
||||
goto remove_af4_failure;
|
||||
netlbl_af4list_foreach_rcu(iter4, &entry_map->type_def.addrsel->list4)
|
||||
goto remove_af4_single_addr;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
netlbl_af6list_foreach_rcu(iter6, &entry_map->type_def.addrsel->list6)
|
||||
goto remove_af4_single_addr;
|
||||
#endif /* IPv6 */
|
||||
/* the domain mapping is empty so remove it from the mapping table */
|
||||
netlbl_domhsh_remove_entry(entry_map, audit_info);
|
||||
|
||||
remove_af4_single_addr:
|
||||
rcu_read_unlock();
|
||||
/* yick, we can't use call_rcu here because we don't have a rcu head
|
||||
* pointer but hopefully this should be a rare case so the pause
|
||||
* shouldn't be a problem */
|
||||
synchronize_rcu();
|
||||
entry = netlbl_domhsh_addr4_entry(entry_addr);
|
||||
cipso_v4_doi_putdef(entry->type_def.cipsov4);
|
||||
kfree(entry);
|
||||
return 0;
|
||||
|
||||
remove_af4_failure:
|
||||
rcu_read_unlock();
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_domhsh_remove - Removes an entry from the domain hash table
|
||||
* @domain: the domain to remove
|
||||
|
|
|
|||
|
|
@ -90,6 +90,10 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
|
|||
struct netlbl_audit *audit_info);
|
||||
int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
|
||||
struct netlbl_audit *audit_info);
|
||||
int netlbl_domhsh_remove_af4(const char *domain,
|
||||
const struct in_addr *addr,
|
||||
const struct in_addr *mask,
|
||||
struct netlbl_audit *audit_info);
|
||||
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
|
||||
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
|
||||
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/netlabel.h>
|
||||
#include <net/cipso_ipv4.h>
|
||||
#include <asm/bug.h>
|
||||
|
|
@ -42,6 +45,7 @@
|
|||
#include "netlabel_cipso_v4.h"
|
||||
#include "netlabel_user.h"
|
||||
#include "netlabel_mgmt.h"
|
||||
#include "netlabel_addrlist.h"
|
||||
|
||||
/*
|
||||
* Configuration Functions
|
||||
|
|
@ -50,6 +54,9 @@
|
|||
/**
|
||||
* netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping
|
||||
* @domain: the domain mapping to remove
|
||||
* @family: address family
|
||||
* @addr: IP address
|
||||
* @mask: IP address mask
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
|
|
@ -58,14 +65,32 @@
|
|||
* values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
|
||||
int netlbl_cfg_map_del(const char *domain,
|
||||
u16 family,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
return netlbl_domhsh_remove(domain, audit_info);
|
||||
if (addr == NULL && mask == NULL) {
|
||||
return netlbl_domhsh_remove(domain, audit_info);
|
||||
} else if (addr != NULL && mask != NULL) {
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
return netlbl_domhsh_remove_af4(domain, addr, mask,
|
||||
audit_info);
|
||||
default:
|
||||
return -EPFNOSUPPORT;
|
||||
}
|
||||
} else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_cfg_unlbl_add_map - Add an unlabeled NetLabel/LSM domain mapping
|
||||
* netlbl_cfg_unlbl_map_add - Add a new unlabeled mapping
|
||||
* @domain: the domain mapping to add
|
||||
* @family: address family
|
||||
* @addr: IP address
|
||||
* @mask: IP address mask
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
|
|
@ -74,11 +99,19 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
|
|||
* negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_unlbl_add_map(const char *domain,
|
||||
int netlbl_cfg_unlbl_map_add(const char *domain,
|
||||
u16 family,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -ENOMEM;
|
||||
struct netlbl_dom_map *entry;
|
||||
struct netlbl_domaddr_map *addrmap = NULL;
|
||||
struct netlbl_domaddr4_map *map4 = NULL;
|
||||
struct netlbl_domaddr6_map *map6 = NULL;
|
||||
const struct in_addr *addr4, *mask4;
|
||||
const struct in6_addr *addr6, *mask6;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (entry == NULL)
|
||||
|
|
@ -86,49 +119,225 @@ int netlbl_cfg_unlbl_add_map(const char *domain,
|
|||
if (domain != NULL) {
|
||||
entry->domain = kstrdup(domain, GFP_ATOMIC);
|
||||
if (entry->domain == NULL)
|
||||
goto cfg_unlbl_add_map_failure;
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
}
|
||||
|
||||
if (addr == NULL && mask == NULL)
|
||||
entry->type = NETLBL_NLTYPE_UNLABELED;
|
||||
else if (addr != NULL && mask != NULL) {
|
||||
addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
|
||||
if (addrmap == NULL)
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
INIT_LIST_HEAD(&addrmap->list4);
|
||||
INIT_LIST_HEAD(&addrmap->list6);
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
addr4 = addr;
|
||||
mask4 = mask;
|
||||
map4 = kzalloc(sizeof(*map4), GFP_ATOMIC);
|
||||
if (map4 == NULL)
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
map4->type = NETLBL_NLTYPE_UNLABELED;
|
||||
map4->list.addr = addr4->s_addr & mask4->s_addr;
|
||||
map4->list.mask = mask4->s_addr;
|
||||
map4->list.valid = 1;
|
||||
ret_val = netlbl_af4list_add(&map4->list,
|
||||
&addrmap->list4);
|
||||
if (ret_val != 0)
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr6 = addr;
|
||||
mask6 = mask;
|
||||
map6 = kzalloc(sizeof(*map6), GFP_ATOMIC);
|
||||
if (map4 == NULL)
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
map6->type = NETLBL_NLTYPE_UNLABELED;
|
||||
ipv6_addr_copy(&map6->list.addr, addr6);
|
||||
map6->list.addr.s6_addr32[0] &= mask6->s6_addr32[0];
|
||||
map6->list.addr.s6_addr32[1] &= mask6->s6_addr32[1];
|
||||
map6->list.addr.s6_addr32[2] &= mask6->s6_addr32[2];
|
||||
map6->list.addr.s6_addr32[3] &= mask6->s6_addr32[3];
|
||||
ipv6_addr_copy(&map6->list.mask, mask6);
|
||||
map6->list.valid = 1;
|
||||
ret_val = netlbl_af4list_add(&map4->list,
|
||||
&addrmap->list4);
|
||||
if (ret_val != 0)
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
break;
|
||||
default:
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
break;
|
||||
}
|
||||
|
||||
entry->type_def.addrsel = addrmap;
|
||||
entry->type = NETLBL_NLTYPE_ADDRSELECT;
|
||||
} else {
|
||||
ret_val = -EINVAL;
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
}
|
||||
entry->type = NETLBL_NLTYPE_UNLABELED;
|
||||
|
||||
ret_val = netlbl_domhsh_add(entry, audit_info);
|
||||
if (ret_val != 0)
|
||||
goto cfg_unlbl_add_map_failure;
|
||||
goto cfg_unlbl_map_add_failure;
|
||||
|
||||
return 0;
|
||||
|
||||
cfg_unlbl_add_map_failure:
|
||||
cfg_unlbl_map_add_failure:
|
||||
if (entry != NULL)
|
||||
kfree(entry->domain);
|
||||
kfree(entry);
|
||||
kfree(addrmap);
|
||||
kfree(map4);
|
||||
kfree(map6);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
|
||||
* @doi_def: the DOI definition
|
||||
* @domain: the domain mapping to add
|
||||
* netlbl_cfg_unlbl_static_add - Adds a new static label
|
||||
* @net: network namespace
|
||||
* @dev_name: interface name
|
||||
* @addr: IP address in network byte order (struct in[6]_addr)
|
||||
* @mask: address mask in network byte order (struct in[6]_addr)
|
||||
* @family: address family
|
||||
* @secid: LSM secid value for the entry
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this
|
||||
* new DOI definition to the NetLabel subsystem. A @domain value of NULL adds
|
||||
* a new default domain mapping. Returns zero on success, negative values on
|
||||
* failure.
|
||||
* Adds a new NetLabel static label to be used when protocol provided labels
|
||||
* are not present on incoming traffic. If @dev_name is NULL then the default
|
||||
* interface will be used. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
|
||||
int netlbl_cfg_unlbl_static_add(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u16 family,
|
||||
u32 secid,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
u32 addr_len;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
addr_len = sizeof(struct in_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr_len = sizeof(struct in6_addr);
|
||||
break;
|
||||
default:
|
||||
return -EPFNOSUPPORT;
|
||||
}
|
||||
|
||||
return netlbl_unlhsh_add(net,
|
||||
dev_name, addr, mask, addr_len,
|
||||
secid, audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_cfg_unlbl_static_del - Removes an existing static label
|
||||
* @net: network namespace
|
||||
* @dev_name: interface name
|
||||
* @addr: IP address in network byte order (struct in[6]_addr)
|
||||
* @mask: address mask in network byte order (struct in[6]_addr)
|
||||
* @family: address family
|
||||
* @secid: LSM secid value for the entry
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Removes an existing NetLabel static label used when protocol provided labels
|
||||
* are not present on incoming traffic. If @dev_name is NULL then the default
|
||||
* interface will be used. Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_unlbl_static_del(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u16 family,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
u32 addr_len;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
addr_len = sizeof(struct in_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr_len = sizeof(struct in6_addr);
|
||||
break;
|
||||
default:
|
||||
return -EPFNOSUPPORT;
|
||||
}
|
||||
|
||||
return netlbl_unlhsh_remove(net,
|
||||
dev_name, addr, mask, addr_len,
|
||||
audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
|
||||
* @doi_def: CIPSO DOI definition
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Add a new CIPSO DOI definition as defined by @doi_def. Returns zero on
|
||||
* success and negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
return cipso_v4_doi_add(doi_def, audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_cfg_cipsov4_del - Remove an existing CIPSOv4 DOI definition
|
||||
* @doi: CIPSO DOI
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Remove an existing CIPSO DOI definition matching @doi. Returns zero on
|
||||
* success and negative values on failure.
|
||||
*
|
||||
*/
|
||||
void netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
|
||||
{
|
||||
cipso_v4_doi_remove(doi, audit_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_cfg_cipsov4_map_add - Add a new CIPSOv4 DOI mapping
|
||||
* @doi: the CIPSO DOI
|
||||
* @domain: the domain mapping to add
|
||||
* @addr: IP address
|
||||
* @mask: IP address mask
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Add a new NetLabel/LSM domain mapping for the given CIPSO DOI to the NetLabel
|
||||
* subsystem. A @domain value of NULL adds a new default domain mapping.
|
||||
* Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_cfg_cipsov4_map_add(u32 doi,
|
||||
const char *domain,
|
||||
const struct in_addr *addr,
|
||||
const struct in_addr *mask,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val = -ENOMEM;
|
||||
u32 doi;
|
||||
u32 doi_type;
|
||||
struct cipso_v4_doi *doi_def;
|
||||
struct netlbl_dom_map *entry;
|
||||
const char *type_str;
|
||||
struct audit_buffer *audit_buf;
|
||||
struct netlbl_domaddr_map *addrmap = NULL;
|
||||
struct netlbl_domaddr4_map *addrinfo = NULL;
|
||||
|
||||
doi = doi_def->doi;
|
||||
doi_type = doi_def->type;
|
||||
doi_def = cipso_v4_doi_getdef(doi);
|
||||
if (doi_def == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (entry == NULL)
|
||||
|
|
@ -136,56 +345,52 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
|
|||
if (domain != NULL) {
|
||||
entry->domain = kstrdup(domain, GFP_ATOMIC);
|
||||
if (entry->domain == NULL)
|
||||
goto cfg_cipsov4_add_map_failure;
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
}
|
||||
|
||||
ret_val = cipso_v4_doi_add(doi_def);
|
||||
if (ret_val != 0)
|
||||
goto cfg_cipsov4_add_map_failure_remove_doi;
|
||||
entry->type = NETLBL_NLTYPE_CIPSOV4;
|
||||
entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi);
|
||||
if (entry->type_def.cipsov4 == NULL) {
|
||||
ret_val = -ENOENT;
|
||||
goto cfg_cipsov4_add_map_failure_remove_doi;
|
||||
if (addr == NULL && mask == NULL) {
|
||||
entry->type_def.cipsov4 = doi_def;
|
||||
entry->type = NETLBL_NLTYPE_CIPSOV4;
|
||||
} else if (addr != NULL && mask != NULL) {
|
||||
addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
|
||||
if (addrmap == NULL)
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
INIT_LIST_HEAD(&addrmap->list4);
|
||||
INIT_LIST_HEAD(&addrmap->list6);
|
||||
|
||||
addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC);
|
||||
if (addrinfo == NULL)
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
addrinfo->type_def.cipsov4 = doi_def;
|
||||
addrinfo->type = NETLBL_NLTYPE_CIPSOV4;
|
||||
addrinfo->list.addr = addr->s_addr & mask->s_addr;
|
||||
addrinfo->list.mask = mask->s_addr;
|
||||
addrinfo->list.valid = 1;
|
||||
ret_val = netlbl_af4list_add(&addrinfo->list, &addrmap->list4);
|
||||
if (ret_val != 0)
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
|
||||
entry->type_def.addrsel = addrmap;
|
||||
entry->type = NETLBL_NLTYPE_ADDRSELECT;
|
||||
} else {
|
||||
ret_val = -EINVAL;
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
}
|
||||
|
||||
ret_val = netlbl_domhsh_add(entry, audit_info);
|
||||
if (ret_val != 0)
|
||||
goto cfg_cipsov4_add_map_failure_release_doi;
|
||||
goto cfg_cipsov4_map_add_failure;
|
||||
|
||||
cfg_cipsov4_add_map_return:
|
||||
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
|
||||
audit_info);
|
||||
if (audit_buf != NULL) {
|
||||
switch (doi_type) {
|
||||
case CIPSO_V4_MAP_TRANS:
|
||||
type_str = "trans";
|
||||
break;
|
||||
case CIPSO_V4_MAP_PASS:
|
||||
type_str = "pass";
|
||||
break;
|
||||
case CIPSO_V4_MAP_LOCAL:
|
||||
type_str = "local";
|
||||
break;
|
||||
default:
|
||||
type_str = "(unknown)";
|
||||
}
|
||||
audit_log_format(audit_buf,
|
||||
" cipso_doi=%u cipso_type=%s res=%u",
|
||||
doi, type_str, ret_val == 0 ? 1 : 0);
|
||||
audit_log_end(audit_buf);
|
||||
}
|
||||
return 0;
|
||||
|
||||
return ret_val;
|
||||
|
||||
cfg_cipsov4_add_map_failure_release_doi:
|
||||
cfg_cipsov4_map_add_failure:
|
||||
cipso_v4_doi_putdef(doi_def);
|
||||
cfg_cipsov4_add_map_failure_remove_doi:
|
||||
cipso_v4_doi_remove(doi, audit_info);
|
||||
cfg_cipsov4_add_map_failure:
|
||||
if (entry != NULL)
|
||||
kfree(entry->domain);
|
||||
kfree(entry);
|
||||
goto cfg_cipsov4_add_map_return;
|
||||
kfree(addrmap);
|
||||
kfree(addrinfo);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -690,6 +895,28 @@ int netlbl_cache_add(const struct sk_buff *skb,
|
|||
return -ENOMSG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol Engine Functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* netlbl_audit_start - Start an audit message
|
||||
* @type: audit message type
|
||||
* @audit_info: NetLabel audit information
|
||||
*
|
||||
* Description:
|
||||
* Start an audit message using the type specified in @type and fill the audit
|
||||
* message with some fields common to all NetLabel audit messages. This
|
||||
* function should only be used by protocol engines, not LSMs. Returns a
|
||||
* pointer to the audit buffer on success, NULL on failure.
|
||||
*
|
||||
*/
|
||||
struct audit_buffer *netlbl_audit_start(int type,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
return netlbl_audit_start_common(type, audit_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup Functions
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -450,13 +450,13 @@ add_iface_failure:
|
|||
* success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
static int netlbl_unlhsh_add(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
u32 secid,
|
||||
struct netlbl_audit *audit_info)
|
||||
int netlbl_unlhsh_add(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
u32 secid,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val;
|
||||
int ifindex;
|
||||
|
|
@ -720,12 +720,12 @@ unlhsh_condremove_failure:
|
|||
* Returns zero on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
static int netlbl_unlhsh_remove(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
struct netlbl_audit *audit_info)
|
||||
int netlbl_unlhsh_remove(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
struct netlbl_audit *audit_info)
|
||||
{
|
||||
int ret_val;
|
||||
struct net_device *dev;
|
||||
|
|
|
|||
|
|
@ -221,6 +221,21 @@ int netlbl_unlabel_genl_init(void);
|
|||
/* General Unlabeled init function */
|
||||
int netlbl_unlabel_init(u32 size);
|
||||
|
||||
/* Static/Fallback label management functions */
|
||||
int netlbl_unlhsh_add(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
u32 secid,
|
||||
struct netlbl_audit *audit_info);
|
||||
int netlbl_unlhsh_remove(struct net *net,
|
||||
const char *dev_name,
|
||||
const void *addr,
|
||||
const void *mask,
|
||||
u32 addr_len,
|
||||
struct netlbl_audit *audit_info);
|
||||
|
||||
/* Process Unlabeled incoming network packets */
|
||||
int netlbl_unlabel_getattr(const struct sk_buff *skb,
|
||||
u16 family,
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ void genl_unregister_mc_group(struct genl_family *family,
|
|||
__genl_unregister_mc_group(family, grp);
|
||||
genl_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(genl_unregister_mc_group);
|
||||
|
||||
static void genl_unregister_mc_groups(struct genl_family *family)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ void sctp_auth_destroy_keys(struct list_head *keys)
|
|||
/* Compare two byte vectors as numbers. Return values
|
||||
* are:
|
||||
* 0 - vectors are equal
|
||||
* < 0 - vector 1 is smaller then vector2
|
||||
* > 0 - vector 1 is greater then vector2
|
||||
* < 0 - vector 1 is smaller than vector2
|
||||
* > 0 - vector 1 is greater than vector2
|
||||
*
|
||||
* Algorithm is:
|
||||
* This is performed by selecting the numerically smaller key vector...
|
||||
|
|
|
|||
|
|
@ -4268,9 +4268,9 @@ nomem:
|
|||
|
||||
/*
|
||||
* Handle a protocol violation when the chunk length is invalid.
|
||||
* "Invalid" length is identified as smaller then the minimal length a
|
||||
* "Invalid" length is identified as smaller than the minimal length a
|
||||
* given chunk can be. For example, a SACK chunk has invalid length
|
||||
* if it's length is set to be smaller then the size of sctp_sack_chunk_t.
|
||||
* if its length is set to be smaller than the size of sctp_sack_chunk_t.
|
||||
*
|
||||
* We inform the other end by sending an ABORT with a Protocol Violation
|
||||
* error code.
|
||||
|
|
@ -4300,7 +4300,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
|
|||
|
||||
/*
|
||||
* Handle a protocol violation when the parameter length is invalid.
|
||||
* "Invalid" length is identified as smaller then the minimal length a
|
||||
* "Invalid" length is identified as smaller than the minimal length a
|
||||
* given parameter can be.
|
||||
*/
|
||||
static sctp_disposition_t sctp_sf_violation_paramlen(
|
||||
|
|
|
|||
|
|
@ -2717,7 +2717,7 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int o
|
|||
paths++;
|
||||
}
|
||||
|
||||
/* Only validate asocmaxrxt if we have more then
|
||||
/* Only validate asocmaxrxt if we have more than
|
||||
* one path/transport. We do this because path
|
||||
* retransmissions are only counted when we have more
|
||||
* then one path.
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn)
|
|||
*/
|
||||
bitmap_zero(map->tsn_map, map->len);
|
||||
} else {
|
||||
/* If the gap is smaller then the map size,
|
||||
/* If the gap is smaller than the map size,
|
||||
* shift the map by 'gap' bits and update further.
|
||||
*/
|
||||
bitmap_shift_right(map->tsn_map, map->tsn_map, gap, map->len);
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
|
|||
|
||||
return new;
|
||||
}
|
||||
EXPORT_SYMBOL(sunrpc_cache_lookup);
|
||||
EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
|
||||
|
||||
|
||||
static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
|
||||
|
|
@ -173,7 +173,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
|
|||
cache_put(old, detail);
|
||||
return tmp;
|
||||
}
|
||||
EXPORT_SYMBOL(sunrpc_cache_update);
|
||||
EXPORT_SYMBOL_GPL(sunrpc_cache_update);
|
||||
|
||||
static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
|
||||
/*
|
||||
|
|
@ -245,7 +245,7 @@ int cache_check(struct cache_detail *detail,
|
|||
cache_put(h, detail);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(cache_check);
|
||||
EXPORT_SYMBOL_GPL(cache_check);
|
||||
|
||||
/*
|
||||
* caches need to be periodically cleaned.
|
||||
|
|
@ -373,7 +373,7 @@ int cache_register(struct cache_detail *cd)
|
|||
schedule_delayed_work(&cache_cleaner, 0);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cache_register);
|
||||
EXPORT_SYMBOL_GPL(cache_register);
|
||||
|
||||
void cache_unregister(struct cache_detail *cd)
|
||||
{
|
||||
|
|
@ -399,7 +399,7 @@ void cache_unregister(struct cache_detail *cd)
|
|||
out:
|
||||
printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
|
||||
}
|
||||
EXPORT_SYMBOL(cache_unregister);
|
||||
EXPORT_SYMBOL_GPL(cache_unregister);
|
||||
|
||||
/* clean cache tries to find something to clean
|
||||
* and cleans it.
|
||||
|
|
@ -514,7 +514,7 @@ void cache_flush(void)
|
|||
while (cache_clean() != -1)
|
||||
cond_resched();
|
||||
}
|
||||
EXPORT_SYMBOL(cache_flush);
|
||||
EXPORT_SYMBOL_GPL(cache_flush);
|
||||
|
||||
void cache_purge(struct cache_detail *detail)
|
||||
{
|
||||
|
|
@ -523,7 +523,7 @@ void cache_purge(struct cache_detail *detail)
|
|||
cache_flush();
|
||||
detail->flush_time = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(cache_purge);
|
||||
EXPORT_SYMBOL_GPL(cache_purge);
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -988,7 +988,7 @@ void qword_add(char **bpp, int *lp, char *str)
|
|||
*bpp = bp;
|
||||
*lp = len;
|
||||
}
|
||||
EXPORT_SYMBOL(qword_add);
|
||||
EXPORT_SYMBOL_GPL(qword_add);
|
||||
|
||||
void qword_addhex(char **bpp, int *lp, char *buf, int blen)
|
||||
{
|
||||
|
|
@ -1017,7 +1017,7 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen)
|
|||
*bpp = bp;
|
||||
*lp = len;
|
||||
}
|
||||
EXPORT_SYMBOL(qword_addhex);
|
||||
EXPORT_SYMBOL_GPL(qword_addhex);
|
||||
|
||||
static void warn_no_listener(struct cache_detail *detail)
|
||||
{
|
||||
|
|
@ -1140,7 +1140,7 @@ int qword_get(char **bpp, char *dest, int bufsize)
|
|||
*dest = '\0';
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(qword_get);
|
||||
EXPORT_SYMBOL_GPL(qword_get);
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) {
|
|||
seq_putc(seq, '\n');
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(svc_seq_show);
|
||||
EXPORT_SYMBOL_GPL(svc_seq_show);
|
||||
|
||||
/**
|
||||
* rpc_alloc_iostats - allocate an rpc_iostats structure
|
||||
|
|
@ -249,14 +249,14 @@ svc_proc_register(struct svc_stat *statp, const struct file_operations *fops)
|
|||
{
|
||||
return do_register(statp->program->pg_name, statp, fops);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_proc_register);
|
||||
EXPORT_SYMBOL_GPL(svc_proc_register);
|
||||
|
||||
void
|
||||
svc_proc_unregister(const char *name)
|
||||
{
|
||||
remove_proc_entry(name, proc_net_rpc);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_proc_unregister);
|
||||
EXPORT_SYMBOL_GPL(svc_proc_unregister);
|
||||
|
||||
void
|
||||
rpc_proc_init(void)
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize,
|
|||
{
|
||||
return __svc_create(prog, bufsize, /*npools*/1, family, shutdown);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_create);
|
||||
EXPORT_SYMBOL_GPL(svc_create);
|
||||
|
||||
struct svc_serv *
|
||||
svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
|
||||
|
|
@ -450,7 +450,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
|
|||
|
||||
return serv;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_create_pooled);
|
||||
EXPORT_SYMBOL_GPL(svc_create_pooled);
|
||||
|
||||
/*
|
||||
* Destroy an RPC service. Should be called with appropriate locking to
|
||||
|
|
@ -492,7 +492,7 @@ svc_destroy(struct svc_serv *serv)
|
|||
kfree(serv->sv_pools);
|
||||
kfree(serv);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_destroy);
|
||||
EXPORT_SYMBOL_GPL(svc_destroy);
|
||||
|
||||
/*
|
||||
* Allocate an RPC server's buffer space.
|
||||
|
|
@ -567,7 +567,7 @@ out_thread:
|
|||
out_enomem:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_prepare_thread);
|
||||
EXPORT_SYMBOL_GPL(svc_prepare_thread);
|
||||
|
||||
/*
|
||||
* Choose a pool in which to create a new thread, for svc_set_num_threads
|
||||
|
|
@ -689,7 +689,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
|||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_set_num_threads);
|
||||
EXPORT_SYMBOL_GPL(svc_set_num_threads);
|
||||
|
||||
/*
|
||||
* Called from a server thread as it's exiting. Caller must hold the BKL or
|
||||
|
|
@ -717,7 +717,7 @@ svc_exit_thread(struct svc_rqst *rqstp)
|
|||
if (serv)
|
||||
svc_destroy(serv);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_exit_thread);
|
||||
EXPORT_SYMBOL_GPL(svc_exit_thread);
|
||||
|
||||
#ifdef CONFIG_SUNRPC_REGISTER_V4
|
||||
|
||||
|
|
@ -1231,7 +1231,7 @@ err_bad:
|
|||
svc_putnl(resv, ntohl(rpc_stat));
|
||||
goto sendit;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_process);
|
||||
EXPORT_SYMBOL_GPL(svc_process);
|
||||
|
||||
/*
|
||||
* Return (transport-specific) limit on the rpc payload.
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
|
|||
svc_xprt_enqueue(xprt);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(svc_reserve);
|
||||
EXPORT_SYMBOL_GPL(svc_reserve);
|
||||
|
||||
static void svc_xprt_release(struct svc_rqst *rqstp)
|
||||
{
|
||||
|
|
@ -448,6 +448,9 @@ static void svc_xprt_release(struct svc_rqst *rqstp)
|
|||
|
||||
rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp);
|
||||
|
||||
kfree(rqstp->rq_deferred);
|
||||
rqstp->rq_deferred = NULL;
|
||||
|
||||
svc_free_res_pages(rqstp);
|
||||
rqstp->rq_res.page_len = 0;
|
||||
rqstp->rq_res.page_base = 0;
|
||||
|
|
@ -498,7 +501,7 @@ void svc_wake_up(struct svc_serv *serv)
|
|||
spin_unlock_bh(&pool->sp_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(svc_wake_up);
|
||||
EXPORT_SYMBOL_GPL(svc_wake_up);
|
||||
|
||||
int svc_port_is_privileged(struct sockaddr *sin)
|
||||
{
|
||||
|
|
@ -515,8 +518,10 @@ int svc_port_is_privileged(struct sockaddr *sin)
|
|||
}
|
||||
|
||||
/*
|
||||
* Make sure that we don't have too many active connections. If we
|
||||
* have, something must be dropped.
|
||||
* Make sure that we don't have too many active connections. If we have,
|
||||
* something must be dropped. It's not clear what will happen if we allow
|
||||
* "too many" connections, but when dealing with network-facing software,
|
||||
* we have to code defensively. Here we do that by imposing hard limits.
|
||||
*
|
||||
* There's no point in trying to do random drop here for DoS
|
||||
* prevention. The NFS clients does 1 reconnect in 15 seconds. An
|
||||
|
|
@ -525,19 +530,27 @@ int svc_port_is_privileged(struct sockaddr *sin)
|
|||
* The only somewhat efficient mechanism would be if drop old
|
||||
* connections from the same IP first. But right now we don't even
|
||||
* record the client IP in svc_sock.
|
||||
*
|
||||
* single-threaded services that expect a lot of clients will probably
|
||||
* need to set sv_maxconn to override the default value which is based
|
||||
* on the number of threads
|
||||
*/
|
||||
static void svc_check_conn_limits(struct svc_serv *serv)
|
||||
{
|
||||
if (serv->sv_tmpcnt > (serv->sv_nrthreads+3)*20) {
|
||||
unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn :
|
||||
(serv->sv_nrthreads+3) * 20;
|
||||
|
||||
if (serv->sv_tmpcnt > limit) {
|
||||
struct svc_xprt *xprt = NULL;
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
if (!list_empty(&serv->sv_tempsocks)) {
|
||||
if (net_ratelimit()) {
|
||||
/* Try to help the admin */
|
||||
printk(KERN_NOTICE "%s: too many open "
|
||||
"connections, consider increasing the "
|
||||
"number of nfsd threads\n",
|
||||
serv->sv_name);
|
||||
"connections, consider increasing %s\n",
|
||||
serv->sv_name, serv->sv_maxconn ?
|
||||
"the max number of connections." :
|
||||
"the number of threads.");
|
||||
}
|
||||
/*
|
||||
* Always select the oldest connection. It's not fair,
|
||||
|
|
@ -730,7 +743,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
|
|||
serv->sv_stats->netcnt++;
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_recv);
|
||||
EXPORT_SYMBOL_GPL(svc_recv);
|
||||
|
||||
/*
|
||||
* Drop request
|
||||
|
|
@ -740,7 +753,7 @@ void svc_drop(struct svc_rqst *rqstp)
|
|||
dprintk("svc: xprt %p dropped request\n", rqstp->rq_xprt);
|
||||
svc_xprt_release(rqstp);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_drop);
|
||||
EXPORT_SYMBOL_GPL(svc_drop);
|
||||
|
||||
/*
|
||||
* Return reply to client.
|
||||
|
|
@ -837,6 +850,11 @@ static void svc_age_temp_xprts(unsigned long closure)
|
|||
void svc_delete_xprt(struct svc_xprt *xprt)
|
||||
{
|
||||
struct svc_serv *serv = xprt->xpt_server;
|
||||
struct svc_deferred_req *dr;
|
||||
|
||||
/* Only do this once */
|
||||
if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
|
||||
return;
|
||||
|
||||
dprintk("svc: svc_delete_xprt(%p)\n", xprt);
|
||||
xprt->xpt_ops->xpo_detach(xprt);
|
||||
|
|
@ -851,12 +869,16 @@ void svc_delete_xprt(struct svc_xprt *xprt)
|
|||
* while still attached to a queue, the queue itself
|
||||
* is about to be destroyed (in svc_destroy).
|
||||
*/
|
||||
if (!test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) {
|
||||
BUG_ON(atomic_read(&xprt->xpt_ref.refcount) < 2);
|
||||
if (test_bit(XPT_TEMP, &xprt->xpt_flags))
|
||||
serv->sv_tmpcnt--;
|
||||
if (test_bit(XPT_TEMP, &xprt->xpt_flags))
|
||||
serv->sv_tmpcnt--;
|
||||
|
||||
for (dr = svc_deferred_dequeue(xprt); dr;
|
||||
dr = svc_deferred_dequeue(xprt)) {
|
||||
svc_xprt_put(xprt);
|
||||
kfree(dr);
|
||||
}
|
||||
|
||||
svc_xprt_put(xprt);
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
}
|
||||
|
||||
|
|
@ -902,17 +924,19 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many)
|
|||
container_of(dreq, struct svc_deferred_req, handle);
|
||||
struct svc_xprt *xprt = dr->xprt;
|
||||
|
||||
if (too_many) {
|
||||
spin_lock(&xprt->xpt_lock);
|
||||
set_bit(XPT_DEFERRED, &xprt->xpt_flags);
|
||||
if (too_many || test_bit(XPT_DEAD, &xprt->xpt_flags)) {
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
dprintk("revisit canceled\n");
|
||||
svc_xprt_put(xprt);
|
||||
kfree(dr);
|
||||
return;
|
||||
}
|
||||
dprintk("revisit queued\n");
|
||||
dr->xprt = NULL;
|
||||
spin_lock(&xprt->xpt_lock);
|
||||
list_add(&dr->handle.recent, &xprt->xpt_deferred);
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
set_bit(XPT_DEFERRED, &xprt->xpt_flags);
|
||||
svc_xprt_enqueue(xprt);
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,13 +57,13 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)
|
|||
rqstp->rq_authop = aops;
|
||||
return aops->accept(rqstp, authp);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_authenticate);
|
||||
EXPORT_SYMBOL_GPL(svc_authenticate);
|
||||
|
||||
int svc_set_client(struct svc_rqst *rqstp)
|
||||
{
|
||||
return rqstp->rq_authop->set_client(rqstp);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_set_client);
|
||||
EXPORT_SYMBOL_GPL(svc_set_client);
|
||||
|
||||
/* A request, which was authenticated, has now executed.
|
||||
* Time to finalise the credentials and verifier
|
||||
|
|
@ -95,7 +95,7 @@ svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
|
|||
spin_unlock(&authtab_lock);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_auth_register);
|
||||
EXPORT_SYMBOL_GPL(svc_auth_register);
|
||||
|
||||
void
|
||||
svc_auth_unregister(rpc_authflavor_t flavor)
|
||||
|
|
@ -105,7 +105,7 @@ svc_auth_unregister(rpc_authflavor_t flavor)
|
|||
authtab[flavor] = NULL;
|
||||
spin_unlock(&authtab_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_auth_unregister);
|
||||
EXPORT_SYMBOL_GPL(svc_auth_unregister);
|
||||
|
||||
/**************************************************
|
||||
* 'auth_domains' are stored in a hash table indexed by name.
|
||||
|
|
@ -132,7 +132,7 @@ void auth_domain_put(struct auth_domain *dom)
|
|||
spin_unlock(&auth_domain_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(auth_domain_put);
|
||||
EXPORT_SYMBOL_GPL(auth_domain_put);
|
||||
|
||||
struct auth_domain *
|
||||
auth_domain_lookup(char *name, struct auth_domain *new)
|
||||
|
|
@ -157,10 +157,10 @@ auth_domain_lookup(char *name, struct auth_domain *new)
|
|||
spin_unlock(&auth_domain_lock);
|
||||
return new;
|
||||
}
|
||||
EXPORT_SYMBOL(auth_domain_lookup);
|
||||
EXPORT_SYMBOL_GPL(auth_domain_lookup);
|
||||
|
||||
struct auth_domain *auth_domain_find(char *name)
|
||||
{
|
||||
return auth_domain_lookup(name, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(auth_domain_find);
|
||||
EXPORT_SYMBOL_GPL(auth_domain_find);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct auth_domain *unix_domain_find(char *name)
|
|||
rv = auth_domain_lookup(name, &new->h);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(unix_domain_find);
|
||||
EXPORT_SYMBOL_GPL(unix_domain_find);
|
||||
|
||||
static void svcauth_unix_domain_release(struct auth_domain *dom)
|
||||
{
|
||||
|
|
@ -358,7 +358,7 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
|
|||
else
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(auth_unix_add_addr);
|
||||
EXPORT_SYMBOL_GPL(auth_unix_add_addr);
|
||||
|
||||
int auth_unix_forget_old(struct auth_domain *dom)
|
||||
{
|
||||
|
|
@ -370,7 +370,7 @@ int auth_unix_forget_old(struct auth_domain *dom)
|
|||
udom->addr_changes++;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(auth_unix_forget_old);
|
||||
EXPORT_SYMBOL_GPL(auth_unix_forget_old);
|
||||
|
||||
struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
|
||||
{
|
||||
|
|
@ -395,13 +395,13 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
|
|||
cache_put(&ipm->h, &ip_map_cache);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(auth_unix_lookup);
|
||||
EXPORT_SYMBOL_GPL(auth_unix_lookup);
|
||||
|
||||
void svcauth_unix_purge(void)
|
||||
{
|
||||
cache_purge(&ip_map_cache);
|
||||
}
|
||||
EXPORT_SYMBOL(svcauth_unix_purge);
|
||||
EXPORT_SYMBOL_GPL(svcauth_unix_purge);
|
||||
|
||||
static inline struct ip_map *
|
||||
ip_map_cached_get(struct svc_rqst *rqstp)
|
||||
|
|
@ -714,7 +714,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
|
|||
return SVC_OK;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(svcauth_unix_set_client);
|
||||
EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
|
||||
|
||||
static int
|
||||
svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ static void svc_udp_data_ready(struct sock *, int);
|
|||
static int svc_udp_recvfrom(struct svc_rqst *);
|
||||
static int svc_udp_sendto(struct svc_rqst *);
|
||||
static void svc_sock_detach(struct svc_xprt *);
|
||||
static void svc_tcp_sock_detach(struct svc_xprt *);
|
||||
static void svc_sock_free(struct svc_xprt *);
|
||||
|
||||
static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
|
||||
|
|
@ -102,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock)
|
|||
static void svc_release_skb(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct sk_buff *skb = rqstp->rq_xprt_ctxt;
|
||||
struct svc_deferred_req *dr = rqstp->rq_deferred;
|
||||
|
||||
if (skb) {
|
||||
struct svc_sock *svsk =
|
||||
|
|
@ -112,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp)
|
|||
dprintk("svc: service %p, releasing skb %p\n", rqstp, skb);
|
||||
skb_free_datagram(svsk->sk_sk, skb);
|
||||
}
|
||||
if (dr) {
|
||||
rqstp->rq_deferred = NULL;
|
||||
kfree(dr);
|
||||
}
|
||||
}
|
||||
|
||||
union svc_pktinfo_u {
|
||||
|
|
@ -289,7 +285,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
|
|||
return -ENOENT;
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(svc_sock_names);
|
||||
EXPORT_SYMBOL_GPL(svc_sock_names);
|
||||
|
||||
/*
|
||||
* Check input queue length
|
||||
|
|
@ -1017,7 +1013,7 @@ static struct svc_xprt_ops svc_tcp_ops = {
|
|||
.xpo_recvfrom = svc_tcp_recvfrom,
|
||||
.xpo_sendto = svc_tcp_sendto,
|
||||
.xpo_release_rqst = svc_release_skb,
|
||||
.xpo_detach = svc_sock_detach,
|
||||
.xpo_detach = svc_tcp_sock_detach,
|
||||
.xpo_free = svc_sock_free,
|
||||
.xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
|
||||
.xpo_has_wspace = svc_tcp_has_wspace,
|
||||
|
|
@ -1101,7 +1097,7 @@ void svc_sock_update_bufs(struct svc_serv *serv)
|
|||
}
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(svc_sock_update_bufs);
|
||||
EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
|
||||
|
||||
/*
|
||||
* Initialize socket for RPC use and create svc_sock struct
|
||||
|
|
@ -1287,6 +1283,24 @@ static void svc_sock_detach(struct svc_xprt *xprt)
|
|||
sk->sk_state_change = svsk->sk_ostate;
|
||||
sk->sk_data_ready = svsk->sk_odata;
|
||||
sk->sk_write_space = svsk->sk_owspace;
|
||||
|
||||
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
|
||||
wake_up_interruptible(sk->sk_sleep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disconnect the socket, and reset the callbacks
|
||||
*/
|
||||
static void svc_tcp_sock_detach(struct svc_xprt *xprt)
|
||||
{
|
||||
struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
|
||||
|
||||
dprintk("svc: svc_tcp_sock_detach(%p)\n", svsk);
|
||||
|
||||
svc_sock_detach(xprt);
|
||||
|
||||
if (!test_bit(XPT_LISTENER, &xprt->xpt_flags))
|
||||
kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
38
net/wimax/Kconfig
Normal file
38
net/wimax/Kconfig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# WiMAX LAN device configuration
|
||||
#
|
||||
|
||||
menuconfig WIMAX
|
||||
tristate "WiMAX Wireless Broadband support"
|
||||
help
|
||||
|
||||
Select to configure support for devices that provide
|
||||
wireless broadband connectivity using the WiMAX protocol
|
||||
(IEEE 802.16).
|
||||
|
||||
Please note that most of these devices require signing up
|
||||
for a service plan with a provider.
|
||||
|
||||
The different WiMAX drivers can be enabled in the menu entry
|
||||
|
||||
Device Drivers > Network device support > WiMAX Wireless
|
||||
Broadband devices
|
||||
|
||||
If unsure, it is safe to select M (module).
|
||||
|
||||
config WIMAX_DEBUG_LEVEL
|
||||
int "WiMAX debug level"
|
||||
depends on WIMAX
|
||||
default 8
|
||||
help
|
||||
|
||||
Select the maximum debug verbosity level to be compiled into
|
||||
the WiMAX stack code.
|
||||
|
||||
By default, debug messages are disabled at runtime and can
|
||||
be selectively enabled for different parts of the code using
|
||||
the sysfs debug-levels file.
|
||||
|
||||
If set at zero, this will compile out all the debug code.
|
||||
|
||||
It is recommended that it is left at 8.
|
||||
13
net/wimax/Makefile
Normal file
13
net/wimax/Makefile
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
obj-$(CONFIG_WIMAX) += wimax.o
|
||||
|
||||
wimax-y := \
|
||||
id-table.o \
|
||||
op-msg.o \
|
||||
op-reset.o \
|
||||
op-rfkill.o \
|
||||
stack.o
|
||||
|
||||
wimax-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
||||
|
||||
42
net/wimax/debug-levels.h
Normal file
42
net/wimax/debug-levels.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Linux WiMAX Stack
|
||||
* Debug levels control file for the wimax module
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
#ifndef __debug_levels__h__
|
||||
#define __debug_levels__h__
|
||||
|
||||
/* Maximum compile and run time debug level for all submodules */
|
||||
#define D_MODULENAME wimax
|
||||
#define D_MASTER CONFIG_WIMAX_DEBUG_LEVEL
|
||||
|
||||
#include <linux/wimax/debug.h>
|
||||
|
||||
/* List of all the enabled modules */
|
||||
enum d_module {
|
||||
D_SUBMODULE_DECLARE(debugfs),
|
||||
D_SUBMODULE_DECLARE(id_table),
|
||||
D_SUBMODULE_DECLARE(op_msg),
|
||||
D_SUBMODULE_DECLARE(op_reset),
|
||||
D_SUBMODULE_DECLARE(op_rfkill),
|
||||
D_SUBMODULE_DECLARE(stack),
|
||||
};
|
||||
|
||||
#endif /* #ifndef __debug_levels__h__ */
|
||||
90
net/wimax/debugfs.c
Normal file
90
net/wimax/debugfs.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Debugfs support
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/wimax.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE debugfs
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/* Debug framework control of debug levels */
|
||||
struct d_level D_LEVEL[] = {
|
||||
D_SUBMODULE_DEFINE(debugfs),
|
||||
D_SUBMODULE_DEFINE(id_table),
|
||||
D_SUBMODULE_DEFINE(op_msg),
|
||||
D_SUBMODULE_DEFINE(op_reset),
|
||||
D_SUBMODULE_DEFINE(op_rfkill),
|
||||
D_SUBMODULE_DEFINE(stack),
|
||||
};
|
||||
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
|
||||
|
||||
#define __debugfs_register(prefix, name, parent) \
|
||||
do { \
|
||||
result = d_level_register_debugfs(prefix, name, parent); \
|
||||
if (result < 0) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
|
||||
|
||||
int wimax_debugfs_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result;
|
||||
struct net_device *net_dev = wimax_dev->net_dev;
|
||||
struct device *dev = net_dev->dev.parent;
|
||||
struct dentry *dentry;
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "wimax:%s", net_dev->name);
|
||||
dentry = debugfs_create_dir(buf, NULL);
|
||||
result = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry)) {
|
||||
if (result == -ENODEV)
|
||||
result = 0; /* No debugfs support */
|
||||
else
|
||||
dev_err(dev, "Can't create debugfs dentry: %d\n",
|
||||
result);
|
||||
goto out;
|
||||
}
|
||||
wimax_dev->debugfs_dentry = dentry;
|
||||
__debugfs_register("wimax_dl_", debugfs, dentry);
|
||||
__debugfs_register("wimax_dl_", id_table, dentry);
|
||||
__debugfs_register("wimax_dl_", op_msg, dentry);
|
||||
__debugfs_register("wimax_dl_", op_reset, dentry);
|
||||
__debugfs_register("wimax_dl_", op_rfkill, dentry);
|
||||
__debugfs_register("wimax_dl_", stack, dentry);
|
||||
result = 0;
|
||||
out:
|
||||
return result;
|
||||
|
||||
error:
|
||||
debugfs_remove_recursive(wimax_dev->debugfs_dentry);
|
||||
return result;
|
||||
}
|
||||
|
||||
void wimax_debugfs_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
debugfs_remove_recursive(wimax_dev->debugfs_dentry);
|
||||
}
|
||||
|
||||
|
||||
142
net/wimax/id-table.c
Normal file
142
net/wimax/id-table.c
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Mappping of generic netlink family IDs to net devices
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* We assign a single generic netlink family ID to each device (to
|
||||
* simplify lookup).
|
||||
*
|
||||
* We need a way to map family ID to a wimax_dev pointer.
|
||||
*
|
||||
* The idea is to use a very simple lookup. Using a netlink attribute
|
||||
* with (for example) the interface name implies a heavier search over
|
||||
* all the network devices; seemed kind of a waste given that we know
|
||||
* we are looking for a WiMAX device and that most systems will have
|
||||
* just a single WiMAX adapter.
|
||||
*
|
||||
* We put all the WiMAX devices in the system in a linked list and
|
||||
* match the generic link family ID against the list.
|
||||
*
|
||||
* By using a linked list, the case of a single adapter in the system
|
||||
* becomes (almost) no overhead, while still working for many more. If
|
||||
* it ever goes beyond two, I'll be surprised.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/wimax.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE id_table
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(wimax_id_table_lock);
|
||||
static struct list_head wimax_id_table = LIST_HEAD_INIT(wimax_id_table);
|
||||
|
||||
|
||||
/*
|
||||
* wimax_id_table_add - add a gennetlink familiy ID / wimax_dev mapping
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor to associate to the Generic
|
||||
* Netlink family ID.
|
||||
*
|
||||
* Look for an empty spot in the ID table; if none found, double the
|
||||
* table's size and get the first spot.
|
||||
*/
|
||||
void wimax_id_table_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_add(&wimax_dev->id_table_node, &wimax_id_table);
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
d_fnend(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wimax_get_netdev_by_info - lookup a wimax_dev from the gennetlink info
|
||||
*
|
||||
* The generic netlink family ID has been filled out in the
|
||||
* nlmsghdr->nlmsg_type field, so we pull it from there, look it up in
|
||||
* the mapping table and reference the wimax_dev.
|
||||
*
|
||||
* When done, the reference should be dropped with
|
||||
* 'dev_put(wimax_dev->net_dev)'.
|
||||
*/
|
||||
struct wimax_dev *wimax_dev_get_by_genl_info(
|
||||
struct genl_info *info, int ifindex)
|
||||
{
|
||||
struct wimax_dev *wimax_dev = NULL;
|
||||
|
||||
d_fnstart(3, NULL, "(info %p ifindex %d)\n", info, ifindex);
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) {
|
||||
if (wimax_dev->net_dev->ifindex == ifindex) {
|
||||
dev_hold(wimax_dev->net_dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wimax_dev == NULL)
|
||||
d_printf(1, NULL, "wimax: no devices found with ifindex %d\n",
|
||||
ifindex);
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
d_fnend(3, NULL, "(info %p ifindex %d) = %p\n",
|
||||
info, ifindex, wimax_dev);
|
||||
return wimax_dev;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wimax_id_table_rm - Remove a gennetlink familiy ID / wimax_dev mapping
|
||||
*
|
||||
* @id: family ID to remove from the table
|
||||
*/
|
||||
void wimax_id_table_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_del_init(&wimax_dev->id_table_node);
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Release the gennetlink family id / mapping table
|
||||
*
|
||||
* On debug, verify that the table is empty upon removal.
|
||||
*/
|
||||
void wimax_id_table_release(void)
|
||||
{
|
||||
#ifndef CONFIG_BUG
|
||||
return;
|
||||
#endif
|
||||
struct wimax_dev *wimax_dev;
|
||||
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) {
|
||||
printk(KERN_ERR "BUG: %s wimax_dev %p ifindex %d not cleared\n",
|
||||
__func__, wimax_dev, wimax_dev->net_dev->ifindex);
|
||||
WARN_ON(1);
|
||||
}
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
}
|
||||
421
net/wimax/op-msg.c
Normal file
421
net/wimax/op-msg.c
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Generic messaging interface between userspace and driver/device
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements a direct communication channel between user space and
|
||||
* the driver/device, by which free form messages can be sent back and
|
||||
* forth.
|
||||
*
|
||||
* This is intended for device-specific features, vendor quirks, etc.
|
||||
*
|
||||
* See include/net/wimax.h
|
||||
*
|
||||
* GENERIC NETLINK ENCODING AND CAPACITY
|
||||
*
|
||||
* A destination "pipe name" is added to each message; it is up to the
|
||||
* drivers to assign or use those names (if using them at all).
|
||||
*
|
||||
* Messages are encoded as a binary netlink attribute using nla_put()
|
||||
* using type NLA_UNSPEC (as some versions of libnl still in
|
||||
* deployment don't yet understand NLA_BINARY).
|
||||
*
|
||||
* The maximum capacity of this transport is PAGESIZE per message (so
|
||||
* the actual payload will be bit smaller depending on the
|
||||
* netlink/generic netlink attributes and headers).
|
||||
*
|
||||
* RECEPTION OF MESSAGES
|
||||
*
|
||||
* When a message is received from user space, it is passed verbatim
|
||||
* to the driver calling wimax_dev->op_msg_from_user(). The return
|
||||
* value from this function is passed back to user space as an ack
|
||||
* over the generic netlink protocol.
|
||||
*
|
||||
* The stack doesn't do any processing or interpretation of these
|
||||
* messages.
|
||||
*
|
||||
* SENDING MESSAGES
|
||||
*
|
||||
* Messages can be sent with wimax_msg().
|
||||
*
|
||||
* If the message delivery needs to happen on a different context to
|
||||
* that of its creation, wimax_msg_alloc() can be used to get a
|
||||
* pointer to the message that can be delivered later on with
|
||||
* wimax_msg_send().
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* wimax_gnl_doit_msg_from_user() Process a message from user space
|
||||
* wimax_dev_get_by_genl_info()
|
||||
* wimax_dev->op_msg_from_user() Delivery of message to the driver
|
||||
*
|
||||
* wimax_msg() Send a message to user space
|
||||
* wimax_msg_alloc()
|
||||
* wimax_msg_send()
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE op_msg
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_alloc - Create a new skb for sending a message to userspace
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
* @pipe_name: "named pipe" the message will be sent to
|
||||
* @msg: pointer to the message data to send
|
||||
* @size: size of the message to send (in bytes), including the header.
|
||||
* @gfp_flags: flags for memory allocation.
|
||||
*
|
||||
* Returns: %0 if ok, negative errno code on error
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Allocates an skb that will contain the message to send to user
|
||||
* space over the messaging pipe and initializes it, copying the
|
||||
* payload.
|
||||
*
|
||||
* Once this call is done, you can deliver it with
|
||||
* wimax_msg_send().
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
|
||||
* wimax_msg_send() depends on skb->data being placed at the
|
||||
* beginning of the user message.
|
||||
*/
|
||||
struct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev,
|
||||
const char *pipe_name,
|
||||
const void *msg, size_t size,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev->net_dev->dev.parent;
|
||||
size_t msg_size;
|
||||
void *genl_msg;
|
||||
struct sk_buff *skb;
|
||||
|
||||
msg_size = nla_total_size(size)
|
||||
+ nla_total_size(sizeof(u32))
|
||||
+ (pipe_name ? nla_total_size(strlen(pipe_name)) : 0);
|
||||
result = -ENOMEM;
|
||||
skb = genlmsg_new(msg_size, gfp_flags);
|
||||
if (skb == NULL)
|
||||
goto error_new;
|
||||
genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family,
|
||||
0, WIMAX_GNL_OP_MSG_TO_USER);
|
||||
if (genl_msg == NULL) {
|
||||
dev_err(dev, "no memory to create generic netlink message\n");
|
||||
goto error_genlmsg_put;
|
||||
}
|
||||
result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX,
|
||||
wimax_dev->net_dev->ifindex);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add ifindex attribute\n");
|
||||
goto error_nla_put;
|
||||
}
|
||||
if (pipe_name) {
|
||||
result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME,
|
||||
pipe_name);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add pipe_name attribute\n");
|
||||
goto error_nla_put;
|
||||
}
|
||||
}
|
||||
result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add payload in attribute\n");
|
||||
goto error_nla_put;
|
||||
}
|
||||
genlmsg_end(skb, genl_msg);
|
||||
return skb;
|
||||
|
||||
error_nla_put:
|
||||
error_genlmsg_put:
|
||||
error_new:
|
||||
nlmsg_free(skb);
|
||||
return ERR_PTR(result);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_alloc);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_data_len - Return a pointer and size of a message's payload
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
* @size: Pointer to where to store the message's size
|
||||
*
|
||||
* Returns the pointer to the message data.
|
||||
*/
|
||||
const void *wimax_msg_data_len(struct sk_buff *msg, size_t *size)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return NULL;
|
||||
}
|
||||
*size = nla_len(nla);
|
||||
return nla_data(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_data_len);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_data - Return a pointer to a message's payload
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
*/
|
||||
const void *wimax_msg_data(struct sk_buff *msg)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return NULL;
|
||||
}
|
||||
return nla_data(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_data);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_len - Return a message's payload length
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
*/
|
||||
ssize_t wimax_msg_len(struct sk_buff *msg)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return nla_len(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_len);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_send - Send a pre-allocated message to user space
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the
|
||||
* ownership of @skb is transferred to this function.
|
||||
*
|
||||
* Returns: 0 if ok, < 0 errno code on error
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Sends a free-form message that was preallocated with
|
||||
* wimax_msg_alloc() and filled up.
|
||||
*
|
||||
* Assumes that once you pass an skb to this function for sending, it
|
||||
* owns it and will release it when done (on success).
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
|
||||
* wimax_msg_send() depends on skb->data being placed at the
|
||||
* beginning of the user message.
|
||||
*/
|
||||
int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev->net_dev->dev.parent;
|
||||
void *msg = skb->data;
|
||||
size_t size = skb->len;
|
||||
might_sleep();
|
||||
|
||||
d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
|
||||
d_dump(2, dev, msg, size);
|
||||
result = genlmsg_multicast(skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
|
||||
d_printf(1, dev, "CTX: genl multicast result %d\n", result);
|
||||
if (result == -ESRCH) /* Nobody connected, ignore it */
|
||||
result = 0; /* btw, the skb is freed already */
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_send);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg - Send a message to user space
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (properly referenced)
|
||||
* @pipe_name: "named pipe" the message will be sent to
|
||||
* @buf: pointer to the message to send.
|
||||
* @size: size of the buffer pointed to by @buf (in bytes).
|
||||
* @gfp_flags: flags for memory allocation.
|
||||
*
|
||||
* Returns: %0 if ok, negative errno code on error.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Sends a free-form message to user space on the device @wimax_dev.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* Once the @skb is given to this function, who will own it and will
|
||||
* release it when done (unless it returns error).
|
||||
*/
|
||||
int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
|
||||
const void *buf, size_t size, gfp_t gfp_flags)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags);
|
||||
if (skb == NULL)
|
||||
goto error_msg_new;
|
||||
result = wimax_msg_send(wimax_dev, skb);
|
||||
error_msg_new:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg);
|
||||
|
||||
|
||||
static const
|
||||
struct nla_policy wimax_gnl_msg_policy[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_MSG_IFIDX] = {
|
||||
.type = NLA_U32,
|
||||
},
|
||||
[WIMAX_GNL_MSG_DATA] = {
|
||||
.type = NLA_UNSPEC, /* libnl doesn't grok BINARY yet */
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Relays a message from user space to the driver
|
||||
*
|
||||
* The skb is passed to the driver-specific function with the netlink
|
||||
* and generic netlink headers already stripped.
|
||||
*
|
||||
* This call will block while handling/relaying the message.
|
||||
*/
|
||||
static
|
||||
int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
struct device *dev;
|
||||
struct nlmsghdr *nlh = info->nlhdr;
|
||||
char *pipe_name;
|
||||
void *msg_buf;
|
||||
size_t msg_len;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) {
|
||||
printk(KERN_ERR "WIMAX_GNL_MSG_FROM_USER: can't find IFIDX "
|
||||
"attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
/* Unpack arguments */
|
||||
result = -EINVAL;
|
||||
if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) {
|
||||
dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA "
|
||||
"attribute\n");
|
||||
goto error_no_data;
|
||||
}
|
||||
msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]);
|
||||
msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]);
|
||||
|
||||
if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL)
|
||||
pipe_name = NULL;
|
||||
else {
|
||||
struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME];
|
||||
size_t attr_len = nla_len(attr);
|
||||
/* libnl-1.1 does not yet support NLA_NUL_STRING */
|
||||
result = -ENOMEM;
|
||||
pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL);
|
||||
if (pipe_name == NULL)
|
||||
goto error_alloc;
|
||||
pipe_name[attr_len] = 0;
|
||||
}
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
result = -ENOSYS;
|
||||
if (wimax_dev->op_msg_from_user == NULL)
|
||||
goto error_noop;
|
||||
|
||||
d_printf(1, dev,
|
||||
"CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n",
|
||||
nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
|
||||
nlh->nlmsg_seq, nlh->nlmsg_pid);
|
||||
d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len);
|
||||
d_dump(2, dev, msg_buf, msg_len);
|
||||
|
||||
result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name,
|
||||
msg_buf, msg_len, info);
|
||||
error_noop:
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
error_alloc:
|
||||
kfree(pipe_name);
|
||||
error_no_data:
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic Netlink glue
|
||||
*/
|
||||
|
||||
struct genl_ops wimax_gnl_msg_from_user = {
|
||||
.cmd = WIMAX_GNL_OP_MSG_FROM_USER,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_msg_policy,
|
||||
.doit = wimax_gnl_doit_msg_from_user,
|
||||
.dumpit = NULL,
|
||||
};
|
||||
|
||||
143
net/wimax/op-reset.c
Normal file
143
net/wimax/op-reset.c
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Implement and export a method for resetting a WiMAX device
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements a simple synchronous call to reset a WiMAX device.
|
||||
*
|
||||
* Resets aim at being warm, keeping the device handles active;
|
||||
* however, when that fails, it falls back to a cold reset (that will
|
||||
* disconnect and reconnect the device).
|
||||
*/
|
||||
|
||||
#include <net/wimax.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_reset
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/**
|
||||
* wimax_reset - Reset a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* %0 if ok and a warm reset was done (the device still exists in
|
||||
* the system).
|
||||
*
|
||||
* -%ENODEV if a cold/bus reset had to be done (device has
|
||||
* disconnected and reconnected, so current handle is not valid
|
||||
* any more).
|
||||
*
|
||||
* -%EINVAL if the device is not even registered.
|
||||
*
|
||||
* Any other negative error code shall be considered as
|
||||
* non-recoverable.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Called when wanting to reset the device for any reason. Device is
|
||||
* taken back to power on status.
|
||||
*
|
||||
* This call blocks; on succesful return, the device has completed the
|
||||
* reset process and is ready to operate.
|
||||
*/
|
||||
int wimax_reset(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result = -EINVAL;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st state;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
dev_hold(wimax_dev->net_dev);
|
||||
state = wimax_dev->state;
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
|
||||
if (state >= WIMAX_ST_DOWN) {
|
||||
mutex_lock(&wimax_dev->mutex_reset);
|
||||
result = wimax_dev->op_reset(wimax_dev);
|
||||
mutex_unlock(&wimax_dev->mutex_reset);
|
||||
}
|
||||
dev_put(wimax_dev->net_dev);
|
||||
|
||||
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(wimax_reset);
|
||||
|
||||
|
||||
static const
|
||||
struct nla_policy wimax_gnl_reset_policy[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_RESET_IFIDX] = {
|
||||
.type = NLA_U32,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Exporting to user space over generic netlink
|
||||
*
|
||||
* Parse the reset command from user space, return error code.
|
||||
*
|
||||
* No attributes.
|
||||
*/
|
||||
static
|
||||
int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
struct device *dev;
|
||||
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_RESET_IFIDX] == NULL) {
|
||||
printk(KERN_ERR "WIMAX_GNL_OP_RFKILL: can't find IFIDX "
|
||||
"attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RESET_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
dev = wimax_dev_to_dev(wimax_dev);
|
||||
/* Execute the operation and send the result back to user space */
|
||||
result = wimax_reset(wimax_dev);
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
struct genl_ops wimax_gnl_reset = {
|
||||
.cmd = WIMAX_GNL_OP_RESET,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_reset_policy,
|
||||
.doit = wimax_gnl_doit_reset,
|
||||
.dumpit = NULL,
|
||||
};
|
||||
532
net/wimax/op-rfkill.c
Normal file
532
net/wimax/op-rfkill.c
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* RF-kill framework integration
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This integrates into the Linux Kernel rfkill susbystem so that the
|
||||
* drivers just have to do the bare minimal work, which is providing a
|
||||
* method to set the software RF-Kill switch and to report changes in
|
||||
* the software and hardware switch status.
|
||||
*
|
||||
* A non-polled generic rfkill device is embedded into the WiMAX
|
||||
* subsystem's representation of a device.
|
||||
*
|
||||
* FIXME: Need polled support? use a timer or add the implementation
|
||||
* to the stack.
|
||||
*
|
||||
* All device drivers have to do is after wimax_dev_init(), call
|
||||
* wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update
|
||||
* initial state and then every time it changes. See wimax.h:struct
|
||||
* wimax_dev for more information.
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* wimax_gnl_doit_rfkill() User space calling wimax_rfkill()
|
||||
* wimax_rfkill() Kernel calling wimax_rfkill()
|
||||
* __wimax_rf_toggle_radio()
|
||||
*
|
||||
* wimax_rfkill_toggle_radio() RF-Kill subsytem calling
|
||||
* __wimax_rf_toggle_radio()
|
||||
*
|
||||
* __wimax_rf_toggle_radio()
|
||||
* wimax_dev->op_rfkill_sw_toggle() Driver backend
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_report_rfkill_sw() Driver reports state change
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_report_rfkill_hw() Driver reports state change
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_rfkill_add() Initialize/shutdown rfkill support
|
||||
* wimax_rfkill_rm() [called by wimax_dev_add/rm()]
|
||||
*/
|
||||
|
||||
#include <net/wimax.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/input.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_rfkill
|
||||
#include "debug-levels.h"
|
||||
|
||||
#ifdef CONFIG_RFKILL
|
||||
|
||||
|
||||
/**
|
||||
* wimax_report_rfkill_hw - Reports changes in the hardware RF switch
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New state of the RF Kill switch. %WIMAX_RF_ON radio on,
|
||||
* %WIMAX_RF_OFF radio off.
|
||||
*
|
||||
* When the device detects a change in the state of thehardware RF
|
||||
* switch, it must call this function to let the WiMAX kernel stack
|
||||
* know that the state has changed so it can be properly propagated.
|
||||
*
|
||||
* The WiMAX stack caches the state (the driver doesn't need to). As
|
||||
* well, as the change is propagated it will come back as a request to
|
||||
* change the software state to mirror the hardware state.
|
||||
*
|
||||
* If the device doesn't have a hardware kill switch, just report
|
||||
* it on initialization as always on (%WIMAX_RF_ON, radio on).
|
||||
*/
|
||||
void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
enum rfkill_state rfkill_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
BUG_ON(state == WIMAX_RF_QUERY);
|
||||
BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
|
||||
if (state != wimax_dev->rf_hw) {
|
||||
wimax_dev->rf_hw = state;
|
||||
rfkill_state = state == WIMAX_RF_ON ?
|
||||
RFKILL_STATE_OFF : RFKILL_STATE_ON;
|
||||
if (wimax_dev->rf_hw == WIMAX_RF_ON
|
||||
&& wimax_dev->rf_sw == WIMAX_RF_ON)
|
||||
wimax_state = WIMAX_ST_READY;
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
input_report_key(wimax_dev->rfkill_input, KEY_WIMAX,
|
||||
rfkill_state);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n",
|
||||
wimax_dev, state, result);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_report_rfkill_sw - Reports changes in the software RF switch
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New state of the RF kill switch. %WIMAX_RF_ON radio on,
|
||||
* %WIMAX_RF_OFF radio off.
|
||||
*
|
||||
* Reports changes in the software RF switch state to the the WiMAX
|
||||
* stack.
|
||||
*
|
||||
* The main use is during initialization, so the driver can query the
|
||||
* device for its current software radio kill switch state and feed it
|
||||
* to the system.
|
||||
*
|
||||
* On the side, the device does not change the software state by
|
||||
* itself. In practice, this can happen, as the device might decide to
|
||||
* switch (in software) the radio off for different reasons.
|
||||
*/
|
||||
void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
BUG_ON(state == WIMAX_RF_QUERY);
|
||||
BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
|
||||
if (state != wimax_dev->rf_sw) {
|
||||
wimax_dev->rf_sw = state;
|
||||
if (wimax_dev->rf_hw == WIMAX_RF_ON
|
||||
&& wimax_dev->rf_sw == WIMAX_RF_ON)
|
||||
wimax_state = WIMAX_ST_READY;
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n",
|
||||
wimax_dev, state, result);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw);
|
||||
|
||||
|
||||
/*
|
||||
* Callback for the RF Kill toggle operation
|
||||
*
|
||||
* This function is called by:
|
||||
*
|
||||
* - The rfkill subsystem when the RF-Kill key is pressed in the
|
||||
* hardware and the driver notifies through
|
||||
* wimax_report_rfkill_hw(). The rfkill subsystem ends up calling back
|
||||
* here so the software RF Kill switch state is changed to reflect
|
||||
* the hardware switch state.
|
||||
*
|
||||
* - When the user sets the state through sysfs' rfkill/state file
|
||||
*
|
||||
* - When the user calls wimax_rfkill().
|
||||
*
|
||||
* This call blocks!
|
||||
*
|
||||
* WARNING! When we call rfkill_unregister(), this will be called with
|
||||
* state 0!
|
||||
*
|
||||
* WARNING: wimax_dev must be locked
|
||||
*/
|
||||
static
|
||||
int __wimax_rf_toggle_radio(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
if (wimax_dev->rf_sw == state)
|
||||
goto out_no_change;
|
||||
if (wimax_dev->op_rfkill_sw_toggle != NULL)
|
||||
result = wimax_dev->op_rfkill_sw_toggle(wimax_dev, state);
|
||||
else if (state == WIMAX_RF_OFF) /* No op? can't turn off */
|
||||
result = -ENXIO;
|
||||
else /* No op? can turn on */
|
||||
result = 0; /* should never happen tho */
|
||||
if (result >= 0) {
|
||||
result = 0;
|
||||
wimax_dev->rf_sw = state;
|
||||
wimax_state = state == WIMAX_RF_ON ?
|
||||
WIMAX_ST_READY : WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
}
|
||||
out_no_change:
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Translate from rfkill state to wimax state
|
||||
*
|
||||
* NOTE: Special state handling rules here
|
||||
*
|
||||
* Just pretend the call didn't happen if we are in a state where
|
||||
* we know for sure it cannot be handled (WIMAX_ST_DOWN or
|
||||
* __WIMAX_ST_QUIESCING). rfkill() needs it to register and
|
||||
* unregister, as it will run this path.
|
||||
*
|
||||
* NOTE: This call will block until the operation is completed.
|
||||
*/
|
||||
static
|
||||
int wimax_rfkill_toggle_radio(void *data, enum rfkill_state state)
|
||||
{
|
||||
int result;
|
||||
struct wimax_dev *wimax_dev = data;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_rf_state rf_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
switch (state) {
|
||||
case RFKILL_STATE_ON:
|
||||
rf_state = WIMAX_RF_OFF;
|
||||
break;
|
||||
case RFKILL_STATE_OFF:
|
||||
rf_state = WIMAX_RF_ON;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
if (wimax_dev->state <= __WIMAX_ST_QUIESCING)
|
||||
result = 0; /* just pretend it didn't happen */
|
||||
else
|
||||
result = __wimax_rf_toggle_radio(wimax_dev, rf_state);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wimax_rfkill - Set the software RF switch state for a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New RF state.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* >= 0 toggle state if ok, < 0 errno code on error. The toggle state
|
||||
* is returned as a bitmap, bit 0 being the hardware RF state, bit 1
|
||||
* the software RF state.
|
||||
*
|
||||
* 0 means disabled (%WIMAX_RF_ON, radio on), 1 means enabled radio
|
||||
* off (%WIMAX_RF_OFF).
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Called by the user when he wants to request the WiMAX radio to be
|
||||
* switched on (%WIMAX_RF_ON) or off (%WIMAX_RF_OFF). With
|
||||
* %WIMAX_RF_QUERY, just the current state is returned.
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* This call will block until the operation is complete.
|
||||
*/
|
||||
int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
switch (state) {
|
||||
case WIMAX_RF_ON:
|
||||
case WIMAX_RF_OFF:
|
||||
result = __wimax_rf_toggle_radio(wimax_dev, state);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
break;
|
||||
case WIMAX_RF_QUERY:
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
result = wimax_dev->rf_sw << 1 | wimax_dev->rf_hw;
|
||||
error:
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(wimax_rfkill);
|
||||
|
||||
|
||||
/*
|
||||
* Register a new WiMAX device's RF Kill support
|
||||
*
|
||||
* WARNING: wimax_dev->mutex must be unlocked
|
||||
*/
|
||||
int wimax_rfkill_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result;
|
||||
struct rfkill *rfkill;
|
||||
struct input_dev *input_dev;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
/* Initialize RF Kill */
|
||||
result = -ENOMEM;
|
||||
rfkill = rfkill_allocate(dev, RFKILL_TYPE_WIMAX);
|
||||
if (rfkill == NULL)
|
||||
goto error_rfkill_allocate;
|
||||
wimax_dev->rfkill = rfkill;
|
||||
|
||||
rfkill->name = wimax_dev->name;
|
||||
rfkill->state = RFKILL_STATE_OFF;
|
||||
rfkill->data = wimax_dev;
|
||||
rfkill->toggle_radio = wimax_rfkill_toggle_radio;
|
||||
rfkill->user_claim_unsupported = 1;
|
||||
|
||||
/* Initialize the input device for the hw key */
|
||||
input_dev = input_allocate_device();
|
||||
if (input_dev == NULL)
|
||||
goto error_input_allocate;
|
||||
wimax_dev->rfkill_input = input_dev;
|
||||
d_printf(1, dev, "rfkill %p input %p\n", rfkill, input_dev);
|
||||
|
||||
input_dev->name = wimax_dev->name;
|
||||
/* FIXME: get a real device bus ID and stuff? do we care? */
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0xffff;
|
||||
input_dev->evbit[0] = BIT(EV_KEY);
|
||||
set_bit(KEY_WIMAX, input_dev->keybit);
|
||||
|
||||
/* Register both */
|
||||
result = input_register_device(wimax_dev->rfkill_input);
|
||||
if (result < 0)
|
||||
goto error_input_register;
|
||||
result = rfkill_register(wimax_dev->rfkill);
|
||||
if (result < 0)
|
||||
goto error_rfkill_register;
|
||||
|
||||
/* If there is no SW toggle op, SW RFKill is always on */
|
||||
if (wimax_dev->op_rfkill_sw_toggle == NULL)
|
||||
wimax_dev->rf_sw = WIMAX_RF_ON;
|
||||
|
||||
d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev);
|
||||
return 0;
|
||||
|
||||
/* if rfkill_register() suceeds, can't use rfkill_free() any
|
||||
* more, only rfkill_unregister() [it owns the refcount]; with
|
||||
* the input device we have the same issue--hence the if. */
|
||||
error_rfkill_register:
|
||||
input_unregister_device(wimax_dev->rfkill_input);
|
||||
wimax_dev->rfkill_input = NULL;
|
||||
error_input_register:
|
||||
if (wimax_dev->rfkill_input)
|
||||
input_free_device(wimax_dev->rfkill_input);
|
||||
error_input_allocate:
|
||||
rfkill_free(wimax_dev->rfkill);
|
||||
error_rfkill_allocate:
|
||||
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deregister a WiMAX device's RF Kill support
|
||||
*
|
||||
* Ick, we can't call rfkill_free() after rfkill_unregister()...oh
|
||||
* well.
|
||||
*
|
||||
* WARNING: wimax_dev->mutex must be unlocked
|
||||
*/
|
||||
void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
rfkill_unregister(wimax_dev->rfkill); /* frees */
|
||||
input_unregister_device(wimax_dev->rfkill_input);
|
||||
d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
}
|
||||
|
||||
|
||||
#else /* #ifdef CONFIG_RFKILL */
|
||||
|
||||
void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw);
|
||||
|
||||
void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw);
|
||||
|
||||
int wimax_rfkill(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
return WIMAX_RF_ON << 1 | WIMAX_RF_ON;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_rfkill);
|
||||
|
||||
int wimax_rfkill_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* #ifdef CONFIG_RFKILL */
|
||||
|
||||
|
||||
/*
|
||||
* Exporting to user space over generic netlink
|
||||
*
|
||||
* Parse the rfkill command from user space, return a combination
|
||||
* value that describe the states of the different toggles.
|
||||
*
|
||||
* Only one attribute: the new state requested (on, off or no change,
|
||||
* just query).
|
||||
*/
|
||||
|
||||
static const
|
||||
struct nla_policy wimax_gnl_rfkill_policy[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_RFKILL_IFIDX] = {
|
||||
.type = NLA_U32,
|
||||
},
|
||||
[WIMAX_GNL_RFKILL_STATE] = {
|
||||
.type = NLA_U32 /* enum wimax_rf_state */
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
struct device *dev;
|
||||
enum wimax_rf_state new_state;
|
||||
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_RFKILL_IFIDX] == NULL) {
|
||||
printk(KERN_ERR "WIMAX_GNL_OP_RFKILL: can't find IFIDX "
|
||||
"attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
dev = wimax_dev_to_dev(wimax_dev);
|
||||
result = -EINVAL;
|
||||
if (info->attrs[WIMAX_GNL_RFKILL_STATE] == NULL) {
|
||||
dev_err(dev, "WIMAX_GNL_RFKILL: can't find RFKILL_STATE "
|
||||
"attribute\n");
|
||||
goto error_no_pid;
|
||||
}
|
||||
new_state = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_STATE]);
|
||||
|
||||
/* Execute the operation and send the result back to user space */
|
||||
result = wimax_rfkill(wimax_dev, new_state);
|
||||
error_no_pid:
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
struct genl_ops wimax_gnl_rfkill = {
|
||||
.cmd = WIMAX_GNL_OP_RFKILL,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_rfkill_policy,
|
||||
.doit = wimax_gnl_doit_rfkill,
|
||||
.dumpit = NULL,
|
||||
};
|
||||
|
||||
599
net/wimax/stack.c
Normal file
599
net/wimax/stack.c
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Initialization, addition and removal of wimax devices
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements:
|
||||
*
|
||||
* - basic life cycle of 'struct wimax_dev' [wimax_dev_*()]; on
|
||||
* addition/registration initialize all subfields and allocate
|
||||
* generic netlink resources for user space communication. On
|
||||
* removal/unregistration, undo all that.
|
||||
*
|
||||
* - device state machine [wimax_state_change()] and support to send
|
||||
* reports to user space when the state changes
|
||||
* [wimax_gnl_re_state_change*()].
|
||||
*
|
||||
* See include/net/wimax.h for rationales and design.
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* [__]wimax_state_change() Called by drivers to update device's state
|
||||
* wimax_gnl_re_state_change_alloc()
|
||||
* wimax_gnl_re_state_change_send()
|
||||
*
|
||||
* wimax_dev_init() Init a device
|
||||
* wimax_dev_add() Register
|
||||
* wimax_rfkill_add()
|
||||
* wimax_gnl_add() Register all the generic netlink resources.
|
||||
* wimax_id_table_add()
|
||||
* wimax_dev_rm() Unregister
|
||||
* wimax_id_table_rm()
|
||||
* wimax_gnl_rm()
|
||||
* wimax_rfkill_rm()
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wimax.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE stack
|
||||
#include "debug-levels.h"
|
||||
|
||||
/*
|
||||
* Authoritative source for the RE_STATE_CHANGE attribute policy
|
||||
*
|
||||
* We don't really use it here, but /me likes to keep the definition
|
||||
* close to where the data is generated.
|
||||
*/
|
||||
/*
|
||||
static const
|
||||
struct nla_policy wimax_gnl_re_status_change[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 },
|
||||
[WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 },
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Allocate a Report State Change message
|
||||
*
|
||||
* @header: save it, you need it for _send()
|
||||
*
|
||||
* Creates and fills a basic state change message; different code
|
||||
* paths can then add more attributes to the message as needed.
|
||||
*
|
||||
* Use wimax_gnl_re_state_change_send() to send the returned skb.
|
||||
*
|
||||
* Returns: skb with the genl message if ok, IS_ERR() ptr on error
|
||||
* with an errno code.
|
||||
*/
|
||||
static
|
||||
struct sk_buff *wimax_gnl_re_state_change_alloc(
|
||||
struct wimax_dev *wimax_dev,
|
||||
enum wimax_st new_state, enum wimax_st old_state,
|
||||
void **header)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
void *data;
|
||||
struct sk_buff *report_skb;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p new_state %u old_state %u)\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
result = -ENOMEM;
|
||||
report_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (report_skb == NULL) {
|
||||
dev_err(dev, "RE_STCH: can't create message\n");
|
||||
goto error_new;
|
||||
}
|
||||
data = genlmsg_put(report_skb, 0, wimax_gnl_mcg.id, &wimax_gnl_family,
|
||||
0, WIMAX_GNL_RE_STATE_CHANGE);
|
||||
if (data == NULL) {
|
||||
dev_err(dev, "RE_STCH: can't put data into message\n");
|
||||
goto error_put;
|
||||
}
|
||||
*header = data;
|
||||
|
||||
result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_OLD, old_state);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding OLD attr: %d\n", result);
|
||||
goto error_put;
|
||||
}
|
||||
result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_NEW, new_state);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding NEW attr: %d\n", result);
|
||||
goto error_put;
|
||||
}
|
||||
result = nla_put_u32(report_skb, WIMAX_GNL_STCH_IFIDX,
|
||||
wimax_dev->net_dev->ifindex);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding IFINDEX attribute\n");
|
||||
goto error_put;
|
||||
}
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %p\n",
|
||||
wimax_dev, new_state, old_state, report_skb);
|
||||
return report_skb;
|
||||
|
||||
error_put:
|
||||
nlmsg_free(report_skb);
|
||||
error_new:
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %d\n",
|
||||
wimax_dev, new_state, old_state, result);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send a Report State Change message (as created with _alloc).
|
||||
*
|
||||
* @report_skb: as returned by wimax_gnl_re_state_change_alloc()
|
||||
* @header: as returned by wimax_gnl_re_state_change_alloc()
|
||||
*
|
||||
* Returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* If the message is NULL, pretend it didn't happen.
|
||||
*/
|
||||
static
|
||||
int wimax_gnl_re_state_change_send(
|
||||
struct wimax_dev *wimax_dev, struct sk_buff *report_skb,
|
||||
void *header)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n",
|
||||
wimax_dev, report_skb);
|
||||
if (report_skb == NULL)
|
||||
goto out;
|
||||
genlmsg_end(report_skb, header);
|
||||
result = genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
|
||||
if (result == -ESRCH) /* Nobody connected, ignore it */
|
||||
result = 0; /* btw, the skb is freed already */
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error sending: %d\n", result);
|
||||
nlmsg_free(report_skb);
|
||||
}
|
||||
out:
|
||||
d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n",
|
||||
wimax_dev, report_skb, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void __check_new_state(enum wimax_st old_state, enum wimax_st new_state,
|
||||
unsigned allowed_states_bm)
|
||||
{
|
||||
if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) {
|
||||
printk(KERN_ERR "SW BUG! Forbidden state change %u -> %u\n",
|
||||
old_state, new_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the current state of a WiMAX device [unlocking version of
|
||||
* wimax_state_change().
|
||||
*/
|
||||
void __wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st old_state = wimax_dev->state;
|
||||
struct sk_buff *stch_skb;
|
||||
void *header;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p new_state %u [old %u])\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
|
||||
if (WARN_ON(new_state >= __WIMAX_ST_INVALID)) {
|
||||
dev_err(dev, "SW BUG: requesting invalid state %u\n",
|
||||
new_state);
|
||||
goto out;
|
||||
}
|
||||
if (old_state == new_state)
|
||||
goto out;
|
||||
header = NULL; /* gcc complains? can't grok why */
|
||||
stch_skb = wimax_gnl_re_state_change_alloc(
|
||||
wimax_dev, new_state, old_state, &header);
|
||||
|
||||
/* Verify the state transition and do exit-from-state actions */
|
||||
switch (old_state) {
|
||||
case __WIMAX_ST_NULL:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << WIMAX_ST_DOWN);
|
||||
break;
|
||||
case WIMAX_ST_DOWN:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_UNINITIALIZED
|
||||
| 1 << WIMAX_ST_RADIO_OFF);
|
||||
break;
|
||||
case __WIMAX_ST_QUIESCING:
|
||||
__check_new_state(old_state, new_state, 1 << WIMAX_ST_DOWN);
|
||||
break;
|
||||
case WIMAX_ST_UNINITIALIZED:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF);
|
||||
break;
|
||||
case WIMAX_ST_RADIO_OFF:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_READY);
|
||||
break;
|
||||
case WIMAX_ST_READY:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_SCANNING
|
||||
| 1 << WIMAX_ST_CONNECTING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_SCANNING:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY
|
||||
| 1 << WIMAX_ST_CONNECTING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_CONNECTING:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY
|
||||
| 1 << WIMAX_ST_SCANNING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_CONNECTED:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY);
|
||||
netif_tx_disable(wimax_dev->net_dev);
|
||||
netif_carrier_off(wimax_dev->net_dev);
|
||||
break;
|
||||
case __WIMAX_ST_INVALID:
|
||||
default:
|
||||
dev_err(dev, "SW BUG: wimax_dev %p is in unknown state %u\n",
|
||||
wimax_dev, wimax_dev->state);
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Execute the actions of entry to the new state */
|
||||
switch (new_state) {
|
||||
case __WIMAX_ST_NULL:
|
||||
dev_err(dev, "SW BUG: wimax_dev %p entering NULL state "
|
||||
"from %u\n", wimax_dev, wimax_dev->state);
|
||||
WARN_ON(1); /* Nobody can enter this state */
|
||||
break;
|
||||
case WIMAX_ST_DOWN:
|
||||
break;
|
||||
case __WIMAX_ST_QUIESCING:
|
||||
break;
|
||||
case WIMAX_ST_UNINITIALIZED:
|
||||
break;
|
||||
case WIMAX_ST_RADIO_OFF:
|
||||
break;
|
||||
case WIMAX_ST_READY:
|
||||
break;
|
||||
case WIMAX_ST_SCANNING:
|
||||
break;
|
||||
case WIMAX_ST_CONNECTING:
|
||||
break;
|
||||
case WIMAX_ST_CONNECTED:
|
||||
netif_carrier_on(wimax_dev->net_dev);
|
||||
netif_wake_queue(wimax_dev->net_dev);
|
||||
break;
|
||||
case __WIMAX_ST_INVALID:
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
__wimax_state_set(wimax_dev, new_state);
|
||||
if (stch_skb)
|
||||
wimax_gnl_re_state_change_send(wimax_dev, stch_skb, header);
|
||||
out:
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u [old %u]) = void\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wimax_state_change - Set the current state of a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (properly referenced)
|
||||
* @new_state: New state to switch to
|
||||
*
|
||||
* This implements the state changes for the wimax devices. It will
|
||||
*
|
||||
* - verify that the state transition is legal (for now it'll just
|
||||
* print a warning if not) according to the table in
|
||||
* linux/wimax.h's documentation for 'enum wimax_st'.
|
||||
*
|
||||
* - perform the actions needed for leaving the current state and
|
||||
* whichever are needed for entering the new state.
|
||||
*
|
||||
* - issue a report to user space indicating the new state (and an
|
||||
* optional payload with information about the new state).
|
||||
*
|
||||
* NOTE: @wimax_dev must be locked
|
||||
*/
|
||||
void wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
|
||||
{
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
__wimax_state_change(wimax_dev, new_state);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_state_change);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_state_get() - Return the current state of a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Returns: Current state of the device according to its driver.
|
||||
*/
|
||||
enum wimax_st wimax_state_get(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
enum wimax_st state;
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
state = wimax_dev->state;
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
return state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_state_get);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_init - initialize a newly allocated instance
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor to initialize.
|
||||
*
|
||||
* Initializes fields of a freshly allocated @wimax_dev instance. This
|
||||
* function assumes that after allocation, the memory occupied by
|
||||
* @wimax_dev was zeroed.
|
||||
*/
|
||||
void wimax_dev_init(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
INIT_LIST_HEAD(&wimax_dev->id_table_node);
|
||||
__wimax_state_set(wimax_dev, WIMAX_ST_UNINITIALIZED);
|
||||
mutex_init(&wimax_dev->mutex);
|
||||
mutex_init(&wimax_dev->mutex_reset);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_init);
|
||||
|
||||
/*
|
||||
* This extern is declared here because it's easier to keep track --
|
||||
* both declarations are a list of the same
|
||||
*/
|
||||
extern struct genl_ops
|
||||
wimax_gnl_msg_from_user,
|
||||
wimax_gnl_reset,
|
||||
wimax_gnl_rfkill;
|
||||
|
||||
static
|
||||
struct genl_ops *wimax_gnl_ops[] = {
|
||||
&wimax_gnl_msg_from_user,
|
||||
&wimax_gnl_reset,
|
||||
&wimax_gnl_rfkill,
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
size_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size,
|
||||
unsigned char *addr, size_t addr_len)
|
||||
{
|
||||
unsigned cnt, total;
|
||||
for (total = cnt = 0; cnt < addr_len; cnt++)
|
||||
total += scnprintf(addr_str + total, addr_str_size - total,
|
||||
"%02x%c", addr[cnt],
|
||||
cnt == addr_len - 1 ? '\0' : ':');
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_add - Register a new WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (as embedded in your @net_dev's
|
||||
* priv data). You must have called wimax_dev_init() on it before.
|
||||
*
|
||||
* @net_dev: net device the @wimax_dev is associated with. The
|
||||
* function expects SET_NETDEV_DEV() and register_netdev() were
|
||||
* already called on it.
|
||||
*
|
||||
* Registers the new WiMAX device, sets up the user-kernel control
|
||||
* interface (generic netlink) and common WiMAX infrastructure.
|
||||
*
|
||||
* Note that the parts that will allow interaction with user space are
|
||||
* setup at the very end, when the rest is in place, as once that
|
||||
* happens, the driver might get user space control requests via
|
||||
* netlink or from debugfs that might translate into calls into
|
||||
* wimax_dev->op_*().
|
||||
*/
|
||||
int wimax_dev_add(struct wimax_dev *wimax_dev, struct net_device *net_dev)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = net_dev->dev.parent;
|
||||
char addr_str[32];
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p net_dev %p)\n", wimax_dev, net_dev);
|
||||
|
||||
/* Do the RFKILL setup before locking, as RFKILL will call
|
||||
* into our functions. */
|
||||
wimax_dev->net_dev = net_dev;
|
||||
result = wimax_rfkill_add(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_rfkill_add;
|
||||
|
||||
/* Set up user-space interaction */
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
wimax_id_table_add(wimax_dev);
|
||||
result = wimax_debugfs_add(wimax_dev);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot initialize debugfs: %d\n",
|
||||
result);
|
||||
goto error_debugfs_add;
|
||||
}
|
||||
|
||||
__wimax_state_set(wimax_dev, WIMAX_ST_DOWN);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
|
||||
wimax_addr_scnprint(addr_str, sizeof(addr_str),
|
||||
net_dev->dev_addr, net_dev->addr_len);
|
||||
dev_err(dev, "WiMAX interface %s (%s) ready\n",
|
||||
net_dev->name, addr_str);
|
||||
d_fnend(3, dev, "(wimax_dev %p net_dev %p) = 0\n", wimax_dev, net_dev);
|
||||
return 0;
|
||||
|
||||
error_debugfs_add:
|
||||
wimax_id_table_rm(wimax_dev);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
wimax_rfkill_rm(wimax_dev);
|
||||
error_rfkill_add:
|
||||
d_fnend(3, dev, "(wimax_dev %p net_dev %p) = %d\n",
|
||||
wimax_dev, net_dev, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_add);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_rm - Unregister an existing WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Unregisters a WiMAX device previously registered for use with
|
||||
* wimax_add_rm().
|
||||
*
|
||||
* IMPORTANT! Must call before calling unregister_netdev().
|
||||
*
|
||||
* After this function returns, you will not get any more user space
|
||||
* control requests (via netlink or debugfs) and thus to wimax_dev->ops.
|
||||
*
|
||||
* Reentrancy control is ensured by setting the state to
|
||||
* %__WIMAX_ST_QUIESCING. rfkill operations coming through
|
||||
* wimax_*rfkill*() will be stopped by the quiescing state; ops coming
|
||||
* from the rfkill subsystem will be stopped by the support being
|
||||
* removed by wimax_rfkill_rm().
|
||||
*/
|
||||
void wimax_dev_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
__wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
|
||||
wimax_debugfs_rm(wimax_dev);
|
||||
wimax_id_table_rm(wimax_dev);
|
||||
__wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
wimax_rfkill_rm(wimax_dev);
|
||||
d_fnend(3, NULL, "(wimax_dev %p) = void\n", wimax_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_rm);
|
||||
|
||||
struct genl_family wimax_gnl_family = {
|
||||
.id = GENL_ID_GENERATE,
|
||||
.name = "WiMAX",
|
||||
.version = WIMAX_GNL_VERSION,
|
||||
.hdrsize = 0,
|
||||
.maxattr = WIMAX_GNL_ATTR_MAX,
|
||||
};
|
||||
|
||||
struct genl_multicast_group wimax_gnl_mcg = {
|
||||
.name = "msg",
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Shutdown the wimax stack */
|
||||
static
|
||||
int __init wimax_subsys_init(void)
|
||||
{
|
||||
int result, cnt;
|
||||
|
||||
d_fnstart(4, NULL, "()\n");
|
||||
snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name),
|
||||
"WiMAX");
|
||||
result = genl_register_family(&wimax_gnl_family);
|
||||
if (unlikely(result < 0)) {
|
||||
printk(KERN_ERR "cannot register generic netlink family: %d\n",
|
||||
result);
|
||||
goto error_register_family;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < ARRAY_SIZE(wimax_gnl_ops); cnt++) {
|
||||
result = genl_register_ops(&wimax_gnl_family,
|
||||
wimax_gnl_ops[cnt]);
|
||||
d_printf(4, NULL, "registering generic netlink op code "
|
||||
"%u: %d\n", wimax_gnl_ops[cnt]->cmd, result);
|
||||
if (unlikely(result < 0)) {
|
||||
printk(KERN_ERR "cannot register generic netlink op "
|
||||
"code %u: %d\n",
|
||||
wimax_gnl_ops[cnt]->cmd, result);
|
||||
goto error_register_ops;
|
||||
}
|
||||
}
|
||||
|
||||
result = genl_register_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
|
||||
if (result < 0)
|
||||
goto error_mc_group;
|
||||
d_fnend(4, NULL, "() = 0\n");
|
||||
return 0;
|
||||
|
||||
error_mc_group:
|
||||
error_register_ops:
|
||||
for (cnt--; cnt >= 0; cnt--)
|
||||
genl_unregister_ops(&wimax_gnl_family,
|
||||
wimax_gnl_ops[cnt]);
|
||||
genl_unregister_family(&wimax_gnl_family);
|
||||
error_register_family:
|
||||
d_fnend(4, NULL, "() = %d\n", result);
|
||||
return result;
|
||||
|
||||
}
|
||||
module_init(wimax_subsys_init);
|
||||
|
||||
|
||||
/* Shutdown the wimax stack */
|
||||
static
|
||||
void __exit wimax_subsys_exit(void)
|
||||
{
|
||||
int cnt;
|
||||
wimax_id_table_release();
|
||||
genl_unregister_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
|
||||
for (cnt = ARRAY_SIZE(wimax_gnl_ops) - 1; cnt >= 0; cnt--)
|
||||
genl_unregister_ops(&wimax_gnl_family,
|
||||
wimax_gnl_ops[cnt]);
|
||||
genl_unregister_family(&wimax_gnl_family);
|
||||
}
|
||||
module_exit(wimax_subsys_exit);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation <[email protected]>");
|
||||
MODULE_DESCRIPTION("Linux WiMAX stack");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
91
net/wimax/wimax-internal.h
Normal file
91
net/wimax/wimax-internal.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Internal API for kernel space WiMAX stack
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This header file is for declarations and definitions internal to
|
||||
* the WiMAX stack. For public APIs and documentation, see
|
||||
* include/net/wimax.h and include/linux/wimax.h.
|
||||
*/
|
||||
|
||||
#ifndef __WIMAX_INTERNAL_H__
|
||||
#define __WIMAX_INTERNAL_H__
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <net/wimax.h>
|
||||
|
||||
|
||||
/*
|
||||
* Decide if a (locked) device is ready for use
|
||||
*
|
||||
* Before using the device structure, it must be locked
|
||||
* (wimax_dev->mutex). As well, most operations need to call this
|
||||
* function to check if the state is the right one.
|
||||
*
|
||||
* An error value will be returned if the state is not the right
|
||||
* one. In that case, the caller should not attempt to use the device
|
||||
* and just unlock it.
|
||||
*/
|
||||
static inline __must_check
|
||||
int wimax_dev_is_ready(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
if (wimax_dev->state == __WIMAX_ST_NULL)
|
||||
return -EINVAL; /* Device is not even registered! */
|
||||
if (wimax_dev->state == WIMAX_ST_DOWN)
|
||||
return -ENOMEDIUM;
|
||||
if (wimax_dev->state == __WIMAX_ST_QUIESCING)
|
||||
return -ESHUTDOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline
|
||||
void __wimax_state_set(struct wimax_dev *wimax_dev, enum wimax_st state)
|
||||
{
|
||||
wimax_dev->state = state;
|
||||
}
|
||||
extern void __wimax_state_change(struct wimax_dev *, enum wimax_st);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern int wimax_debugfs_add(struct wimax_dev *);
|
||||
extern void wimax_debugfs_rm(struct wimax_dev *);
|
||||
#else
|
||||
static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void wimax_debugfs_rm(struct wimax_dev *wimax_dev) {}
|
||||
#endif
|
||||
|
||||
extern void wimax_id_table_add(struct wimax_dev *);
|
||||
extern struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int);
|
||||
extern void wimax_id_table_rm(struct wimax_dev *);
|
||||
extern void wimax_id_table_release(void);
|
||||
|
||||
extern int wimax_rfkill_add(struct wimax_dev *);
|
||||
extern void wimax_rfkill_rm(struct wimax_dev *);
|
||||
|
||||
extern struct genl_family wimax_gnl_family;
|
||||
extern struct genl_multicast_group wimax_gnl_mcg;
|
||||
|
||||
#endif /* #ifdef __KERNEL__ */
|
||||
#endif /* #ifndef __WIMAX_INTERNAL_H__ */
|
||||
Loading…
Reference in a new issue