Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d13ec65 authored by Jon Paul Maloy's avatar Jon Paul Maloy Committed by David S. Miller
Browse files

tipc: introduce link entry structure to struct tipc_node



struct 'tipc_node' currently contains two arrays for link attributes,
one for the link pointers, and one for the usable link MTUs.

We now group those into a new struct 'tipc_link_entry', and intoduce
one single array consisting of such enties. Apart from being a cosmetic
improvement, this is a starting point for the strict master-slave
relation between node and link that we will introduce in the following
commits.

Reviewed-by: default avatarYing Xue <ying.xue@windriver.com>
Signed-off-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6acc2326
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -413,7 +413,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
	 * all nodes in the cluster don't ACK at the same time
	 * all nodes in the cluster don't ACK at the same time
	 */
	 */
	if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {
	if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {
		tipc_link_proto_xmit(node->active_links[node->addr & 1],
		tipc_link_proto_xmit(node_active_link(node, node->addr),
				     STATE_MSG, 0, 0, 0, 0);
				     STATE_MSG, 0, 0, 0, 0);
		tn->bcl->stats.sent_acks++;
		tn->bcl->stats.sent_acks++;
	}
	}
+1 −1
Original line number Original line Diff line number Diff line
@@ -170,7 +170,7 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *buf,
		return;
		return;
	tipc_node_lock(node);
	tipc_node_lock(node);
	node->capabilities = caps;
	node->capabilities = caps;
	link = node->links[bearer->identity];
	link = node->links[bearer->identity].link;


	/* Prepare to validate requesting node's signature and media address */
	/* Prepare to validate requesting node's signature and media address */
	sign_match = (signature == node->signature);
	sign_match = (signature == node->signature);
+32 −28
Original line number Original line Diff line number Diff line
@@ -132,9 +132,11 @@ static void tipc_link_put(struct tipc_link *l_ptr)


static struct tipc_link *tipc_parallel_link(struct tipc_link *l)
static struct tipc_link *tipc_parallel_link(struct tipc_link *l)
{
{
	if (l->owner->active_links[0] != l)
	struct tipc_node *n = l->owner;
		return l->owner->active_links[0];

	return l->owner->active_links[1];
	if (node_active_link(n, 0) != l)
		return node_active_link(n, 0);
	return node_active_link(n, 1);
}
}


/*
/*
@@ -147,10 +149,11 @@ int tipc_link_is_up(struct tipc_link *l_ptr)
	return link_working_working(l_ptr) || link_working_unknown(l_ptr);
	return link_working_working(l_ptr) || link_working_unknown(l_ptr);
}
}


int tipc_link_is_active(struct tipc_link *l_ptr)
int tipc_link_is_active(struct tipc_link *l)
{
{
	return	(l_ptr->owner->active_links[0] == l_ptr) ||
	struct tipc_node *n = l->owner;
		(l_ptr->owner->active_links[1] == l_ptr);

	return (node_active_link(n, 0) == l) || (node_active_link(n, 1) == l);
}
}


/**
/**
@@ -240,7 +243,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
		return NULL;
		return NULL;
	}
	}


	if (n_ptr->links[b_ptr->identity]) {
	if (n_ptr->links[b_ptr->identity].link) {
		tipc_addr_string_fill(addr_string, n_ptr->addr);
		tipc_addr_string_fill(addr_string, n_ptr->addr);
		pr_err("Attempt to establish second link on <%s> to %s\n",
		pr_err("Attempt to establish second link on <%s> to %s\n",
		       b_ptr->name, addr_string);
		       b_ptr->name, addr_string);
@@ -321,7 +324,7 @@ void tipc_link_delete_list(struct net *net, unsigned int bearer_id)
	rcu_read_lock();
	rcu_read_lock();
	list_for_each_entry_rcu(node, &tn->node_list, list) {
	list_for_each_entry_rcu(node, &tn->node_list, list) {
		tipc_node_lock(node);
		tipc_node_lock(node);
		link = node->links[bearer_id];
		link = node->links[bearer_id].link;
		if (link)
		if (link)
			tipc_link_delete(link);
			tipc_link_delete(link);
		tipc_node_unlock(node);
		tipc_node_unlock(node);
@@ -446,7 +449,7 @@ void tipc_link_reset(struct tipc_link *l_ptr)
	if ((prev_state == RESET_UNKNOWN) || (prev_state == RESET_RESET))
	if ((prev_state == RESET_UNKNOWN) || (prev_state == RESET_RESET))
		return;
		return;


	tipc_node_link_down(l_ptr->owner, l_ptr);
	tipc_node_link_down(l_ptr->owner, l_ptr->bearer_id);
	tipc_bearer_remove_dest(owner->net, l_ptr->bearer_id, l_ptr->addr);
	tipc_bearer_remove_dest(owner->net, l_ptr->bearer_id, l_ptr->addr);


	if (was_active_link && tipc_node_is_up(l_ptr->owner) && (pl != l_ptr)) {
	if (was_active_link && tipc_node_is_up(l_ptr->owner) && (pl != l_ptr)) {
@@ -482,7 +485,7 @@ static void link_activate(struct tipc_link *link)
	link->rcv_nxt = 1;
	link->rcv_nxt = 1;
	link->stats.recv_info = 1;
	link->stats.recv_info = 1;
	link->silent_intv_cnt = 0;
	link->silent_intv_cnt = 0;
	tipc_node_link_up(node, link);
	tipc_node_link_up(node, link->bearer_id);
	tipc_bearer_add_dest(node->net, link->bearer_id, link->addr);
	tipc_bearer_add_dest(node->net, link->bearer_id, link->addr);
}
}


@@ -577,7 +580,7 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
		case TRAFFIC_MSG_EVT:
		case TRAFFIC_MSG_EVT:
			break;
			break;
		case ACTIVATE_MSG:
		case ACTIVATE_MSG:
			other = l_ptr->owner->active_links[0];
			other = node_active_link(l_ptr->owner, 0);
			if (other && link_working_unknown(other))
			if (other && link_working_unknown(other))
				break;
				break;
			l_ptr->state = WORKING_WORKING;
			l_ptr->state = WORKING_WORKING;
@@ -606,7 +609,7 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
		switch (event) {
		switch (event) {
		case TRAFFIC_MSG_EVT:
		case TRAFFIC_MSG_EVT:
		case ACTIVATE_MSG:
		case ACTIVATE_MSG:
			other = l_ptr->owner->active_links[0];
			other = node_active_link(l_ptr->owner, 0);
			if (other && link_working_unknown(other))
			if (other && link_working_unknown(other))
				break;
				break;
			l_ptr->state = WORKING_WORKING;
			l_ptr->state = WORKING_WORKING;
@@ -755,7 +758,7 @@ int tipc_link_xmit(struct net *net, struct sk_buff_head *list, u32 dnode,
	node = tipc_node_find(net, dnode);
	node = tipc_node_find(net, dnode);
	if (node) {
	if (node) {
		tipc_node_lock(node);
		tipc_node_lock(node);
		link = node->active_links[selector & 1];
		link = node_active_link(node, selector & 1);
		if (link)
		if (link)
			rc = __tipc_link_xmit(net, link, list);
			rc = __tipc_link_xmit(net, link, list);
		tipc_node_unlock(node);
		tipc_node_unlock(node);
@@ -858,9 +861,9 @@ void tipc_link_reset_all(struct tipc_node *node)
		tipc_addr_string_fill(addr_string, node->addr));
		tipc_addr_string_fill(addr_string, node->addr));


	for (i = 0; i < MAX_BEARERS; i++) {
	for (i = 0; i < MAX_BEARERS; i++) {
		if (node->links[i]) {
		if (node->links[i].link) {
			link_print(node->links[i], "Resetting link\n");
			link_print(node->links[i].link, "Resetting link\n");
			tipc_link_reset(node->links[i]);
			tipc_link_reset(node->links[i].link);
		}
		}
	}
	}


@@ -1029,7 +1032,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr)


		tipc_node_lock(n_ptr);
		tipc_node_lock(n_ptr);
		/* Locate unicast link endpoint that should handle message */
		/* Locate unicast link endpoint that should handle message */
		l_ptr = n_ptr->links[b_ptr->identity];
		l_ptr = n_ptr->links[b_ptr->identity].link;
		if (unlikely(!l_ptr))
		if (unlikely(!l_ptr))
			goto unlock;
			goto unlock;


@@ -1496,7 +1499,7 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
	struct sk_buff *skb;
	struct sk_buff *skb;
	u32 length = msg_size(msg);
	u32 length = msg_size(msg);


	tunnel = l_ptr->owner->active_links[selector & 1];
	tunnel = node_active_link(l_ptr->owner, selector & 1);
	if (!tipc_link_is_up(tunnel)) {
	if (!tipc_link_is_up(tunnel)) {
		pr_warn("%stunnel link no longer available\n", link_co_err);
		pr_warn("%stunnel link no longer available\n", link_co_err);
		return;
		return;
@@ -1522,7 +1525,7 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
{
{
	int msgcount;
	int msgcount;
	struct tipc_link *tunnel = l_ptr->owner->active_links[0];
	struct tipc_link *tunnel = node_active_link(l_ptr->owner, 0);
	struct tipc_msg tunnel_hdr;
	struct tipc_msg tunnel_hdr;
	struct sk_buff *skb;
	struct sk_buff *skb;
	int split_bundles;
	int split_bundles;
@@ -1556,8 +1559,8 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
		return;
		return;
	}
	}


	split_bundles = (l_ptr->owner->active_links[0] !=
	split_bundles = (node_active_link(l_ptr->owner, 0) !=
			 l_ptr->owner->active_links[1]);
			 node_active_link(l_ptr->owner, 0));


	skb_queue_walk(&l_ptr->transmq, skb) {
	skb_queue_walk(&l_ptr->transmq, skb) {
		struct tipc_msg *msg = buf_msg(skb);
		struct tipc_msg *msg = buf_msg(skb);
@@ -1660,7 +1663,7 @@ static bool tipc_link_failover_rcv(struct tipc_link *link,
	if (bearer_id == link->bearer_id)
	if (bearer_id == link->bearer_id)
		goto exit;
		goto exit;


	pl = link->owner->links[bearer_id];
	pl = link->owner->links[bearer_id].link;
	if (pl && tipc_link_is_up(pl))
	if (pl && tipc_link_is_up(pl))
		tipc_link_reset(pl);
		tipc_link_reset(pl);


@@ -1743,7 +1746,7 @@ static struct tipc_node *tipc_link_find_owner(struct net *net,
	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
	list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
		tipc_node_lock(n_ptr);
		tipc_node_lock(n_ptr);
		for (i = 0; i < MAX_BEARERS; i++) {
		for (i = 0; i < MAX_BEARERS; i++) {
			l_ptr = n_ptr->links[i];
			l_ptr = n_ptr->links[i].link;
			if (l_ptr && !strcmp(l_ptr->name, link_name)) {
			if (l_ptr && !strcmp(l_ptr->name, link_name)) {
				*bearer_id = i;
				*bearer_id = i;
				found_node = n_ptr;
				found_node = n_ptr;
@@ -1865,7 +1868,7 @@ int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)


	tipc_node_lock(node);
	tipc_node_lock(node);


	link = node->links[bearer_id];
	link = node->links[bearer_id].link;
	if (!link) {
	if (!link) {
		res = -EINVAL;
		res = -EINVAL;
		goto out;
		goto out;
@@ -2055,10 +2058,11 @@ static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
	for (i = *prev_link; i < MAX_BEARERS; i++) {
	for (i = *prev_link; i < MAX_BEARERS; i++) {
		*prev_link = i;
		*prev_link = i;


		if (!node->links[i])
		if (!node->links[i].link)
			continue;
			continue;


		err = __tipc_nl_add_link(net, msg, node->links[i], NLM_F_MULTI);
		err = __tipc_nl_add_link(net, msg,
					 node->links[i].link, NLM_F_MULTI);
		if (err)
		if (err)
			return err;
			return err;
	}
	}
@@ -2172,7 +2176,7 @@ int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
			return -EINVAL;
			return -EINVAL;


		tipc_node_lock(node);
		tipc_node_lock(node);
		link = node->links[bearer_id];
		link = node->links[bearer_id].link;
		if (!link) {
		if (!link) {
			tipc_node_unlock(node);
			tipc_node_unlock(node);
			nlmsg_free(msg.skb);
			nlmsg_free(msg.skb);
@@ -2227,7 +2231,7 @@ int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)


	tipc_node_lock(node);
	tipc_node_lock(node);


	link = node->links[bearer_id];
	link = node->links[bearer_id].link;
	if (!link) {
	if (!link) {
		tipc_node_unlock(node);
		tipc_node_unlock(node);
		return -EINVAL;
		return -EINVAL;
+1 −1
Original line number Original line Diff line number Diff line
@@ -96,7 +96,7 @@ void named_cluster_distribute(struct net *net, struct sk_buff *skb)
		dnode = node->addr;
		dnode = node->addr;
		if (in_own_node(net, dnode))
		if (in_own_node(net, dnode))
			continue;
			continue;
		if (!tipc_node_active_links(node))
		if (!tipc_node_is_up(node))
			continue;
			continue;
		oskb = pskb_copy(skb, GFP_ATOMIC);
		oskb = pskb_copy(skb, GFP_ATOMIC);
		if (!oskb)
		if (!oskb)
+78 −85
Original line number Original line Diff line number Diff line
@@ -224,126 +224,119 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
 *
 *
 * Link becomes active (alone or shared) or standby, depending on its priority.
 * Link becomes active (alone or shared) or standby, depending on its priority.
 */
 */
void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
void tipc_node_link_up(struct tipc_node *n, int bearer_id)
{
{
	struct tipc_link **active = &n_ptr->active_links[0];
	struct tipc_link_entry **actv = &n->active_links[0];
	struct tipc_link_entry *le = &n->links[bearer_id];
	struct tipc_link *l = le->link;


	n_ptr->working_links++;
	/* Leave room for tunnel header when returning 'mtu' to users: */
	n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP;
	n->links[bearer_id].mtu = l->mtu - INT_H_SIZE;
	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;

	n->working_links++;
	n->action_flags |= TIPC_NOTIFY_LINK_UP;
	n->link_id = l->peer_bearer_id << 16 | l->bearer_id;


	pr_debug("Established link <%s> on network plane %c\n",
	pr_debug("Established link <%s> on network plane %c\n",
		 l_ptr->name, l_ptr->net_plane);
		 l->name, l->net_plane);


	if (!active[0]) {
	/* No active links ? => take both active slots */
		active[0] = active[1] = l_ptr;
	if (!actv[0]) {
		node_established_contact(n_ptr);
		actv[0] = le;
		goto exit;
		actv[1] = le;
		node_established_contact(n);
		return;
	}
	}
	if (l_ptr->priority < active[0]->priority) {
	if (l->priority < actv[0]->link->priority) {
		pr_debug("New link <%s> becomes standby\n", l_ptr->name);
		pr_debug("New link <%s> becomes standby\n", l->name);
		goto exit;
		return;
	}
	}
	tipc_link_dup_queue_xmit(active[0], l_ptr);
	tipc_link_dup_queue_xmit(actv[0]->link, l);
	if (l_ptr->priority == active[0]->priority) {

		active[0] = l_ptr;
	/* Take one active slot if applicable */
		goto exit;
	if (l->priority == actv[0]->link->priority) {
		actv[0] = le;
		return;
	}
	}
	pr_debug("Old link <%s> becomes standby\n", active[0]->name);
	/* Higher prio than current active? => take both active slots */
	if (active[1] != active[0])
	pr_debug("Old l <%s> becomes standby\n", actv[0]->link->name);
		pr_debug("Old link <%s> becomes standby\n", active[1]->name);
	if (actv[1] != actv[0])
	active[0] = active[1] = l_ptr;
		pr_debug("Old link <%s> now standby\n", actv[1]->link->name);
exit:
	actv[0] = le;
	/* Leave room for changeover header when returning 'mtu' to users: */
	actv[1] = le;
	n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
	n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
}
}


/**
/**
 * node_select_active_links - select active link
 * node_select_active_links - select which working links should be active
 */
 */
static void node_select_active_links(struct tipc_node *n_ptr)
static void node_select_active_links(struct tipc_node *n)
{
{
	struct tipc_link **active = &n_ptr->active_links[0];
	struct tipc_link_entry **actv = &n->active_links[0];
	u32 i;
	struct tipc_link *l;
	u32 highest_prio = 0;
	u32 b, highest = 0;


	active[0] = active[1] = NULL;
	actv[0] = NULL;

	actv[1] = NULL;
	for (i = 0; i < MAX_BEARERS; i++) {
		struct tipc_link *l_ptr = n_ptr->links[i];


		if (!l_ptr || !tipc_link_is_up(l_ptr) ||
	for (b = 0; b < MAX_BEARERS; b++) {
		    (l_ptr->priority < highest_prio))
		l = n->links[b].link;
		if (!l || !tipc_link_is_up(l) || (l->priority < highest))
			continue;
		if (l->priority > highest) {
			highest = l->priority;
			actv[0] = &n->links[b];
			actv[1] = &n->links[b];
			continue;
			continue;

		if (l_ptr->priority > highest_prio) {
			highest_prio = l_ptr->priority;
			active[0] = active[1] = l_ptr;
		} else {
			active[1] = l_ptr;
		}
		}
		actv[1] = &n->links[b];
	}
	}
}
}


/**
/**
 * tipc_node_link_down - handle loss of link
 * tipc_node_link_down - handle loss of link
 */
 */
void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
void tipc_node_link_down(struct tipc_node *n, int bearer_id)
{
{
	struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);
	struct tipc_link_entry **actv = &n->active_links[0];
	struct tipc_link **active;
	struct tipc_link_entry *le = &n->links[bearer_id];
	struct tipc_link *l = le->link;


	n_ptr->working_links--;
	n->working_links--;
	n_ptr->action_flags |= TIPC_NOTIFY_LINK_DOWN;
	n->action_flags |= TIPC_NOTIFY_LINK_DOWN;
	n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id;
	n->link_id = l->peer_bearer_id << 16 | l->bearer_id;


	if (!tipc_link_is_active(l_ptr)) {
	if (!tipc_link_is_active(l)) {
		pr_debug("Lost standby link <%s> on network plane %c\n",
		pr_debug("Lost standby link <%s> on network plane %c\n",
			 l_ptr->name, l_ptr->net_plane);
			 l->name, l->net_plane);
		return;
		return;
	}
	}
	pr_debug("Lost link <%s> on network plane %c\n",
	pr_debug("Lost link <%s> on network plane %c\n",
		 l_ptr->name, l_ptr->net_plane);
		 l->name, l->net_plane);

	active = &n_ptr->active_links[0];
	if (active[0] == l_ptr)
		active[0] = active[1];
	if (active[1] == l_ptr)
		active[1] = active[0];
	if (active[0] == l_ptr)
		node_select_active_links(n_ptr);
	if (tipc_node_is_up(n_ptr))
		tipc_link_failover_send_queue(l_ptr);
	else
		node_lost_contact(n_ptr);


	/* Leave room for changeover header when returning 'mtu' to users: */
	/* Resdistribute active slots if applicable */
	if (active[0]) {
	if (actv[0] == le)
		n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
		actv[0] = actv[1];
		n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
	if (actv[1] == le)
		return;
		actv[1] = actv[0];
	}
	/* Loopback link went down? No fragmentation needed from now on. */
	if (n_ptr->addr == tn->own_addr) {
		n_ptr->act_mtus[0] = MAX_MSG_SIZE;
		n_ptr->act_mtus[1] = MAX_MSG_SIZE;
	}
}


int tipc_node_active_links(struct tipc_node *n_ptr)
	/* Last link of this priority? => select other ones if available */
{
	if (actv[0] == le)
	return n_ptr->active_links[0] != NULL;
		node_select_active_links(n);

	if (tipc_node_is_up(n))
		tipc_link_failover_send_queue(l);
	else
		node_lost_contact(n);
}
}


int tipc_node_is_up(struct tipc_node *n_ptr)
bool tipc_node_is_up(struct tipc_node *n)
{
{
	return tipc_node_active_links(n_ptr);
	return n->active_links[0];
}
}


void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
{
{
	n_ptr->links[l_ptr->bearer_id] = l_ptr;
	n_ptr->links[l_ptr->bearer_id].link = l_ptr;
	n_ptr->link_cnt++;
	n_ptr->link_cnt++;
}
}


@@ -352,9 +345,9 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
	int i;
	int i;


	for (i = 0; i < MAX_BEARERS; i++) {
	for (i = 0; i < MAX_BEARERS; i++) {
		if (l_ptr != n_ptr->links[i])
		if (l_ptr != n_ptr->links[i].link)
			continue;
			continue;
		n_ptr->links[i] = NULL;
		n_ptr->links[i].link = NULL;
		n_ptr->link_cnt--;
		n_ptr->link_cnt--;
	}
	}
}
}
@@ -396,7 +389,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)


	/* Abort any ongoing link failover */
	/* Abort any ongoing link failover */
	for (i = 0; i < MAX_BEARERS; i++) {
	for (i = 0; i < MAX_BEARERS; i++) {
		struct tipc_link *l_ptr = n_ptr->links[i];
		struct tipc_link *l_ptr = n_ptr->links[i].link;
		if (!l_ptr)
		if (!l_ptr)
			continue;
			continue;
		l_ptr->flags &= ~LINK_FAILINGOVER;
		l_ptr->flags &= ~LINK_FAILINGOVER;
@@ -453,7 +446,7 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
		goto exit;
		goto exit;


	tipc_node_lock(node);
	tipc_node_lock(node);
	link = node->links[bearer_id];
	link = node->links[bearer_id].link;
	if (link) {
	if (link) {
		strncpy(linkname, link->name, len);
		strncpy(linkname, link->name, len);
		err = 0;
		err = 0;
Loading