* [PATCH RFC net-next] openvswitch: Queue upcalls to userspace in per-port round-robin order
From: Matteo Croce @ 2018-07-04 14:23 UTC (permalink / raw)
To: netdev, dev, Pravin B Shelar; +Cc: Stefano Brivio, Jiri Benc, Aaron Conole
From: Stefano Brivio <sbrivio@redhat.com>
Open vSwitch sends to userspace all received packets that have
no associated flow (thus doing an "upcall"). Then the userspace
program creates a new flow and determines the actions to apply
based on its configuration.
When a single port generates a high rate of upcalls, it can
prevent other ports from dispatching their own upcalls. vswitchd
overcomes this problem by creating many netlink sockets for each
port, but it quickly exceeds any reasonable maximum number of
open files when dealing with huge amounts of ports.
This patch queues all the upcalls into a list, ordering them in
a per-port round-robin fashion, and schedules a deferred work to
queue them to userspace.
The algorithm to queue upcalls in a round-robin fashion,
provided by Stefano, is based on these two rules:
- upcalls for a given port must be inserted after all the other
occurrences of upcalls for the same port already in the queue,
in order to avoid out-of-order upcalls for a given port
- insertion happens once the highest upcall count for any given
port (excluding the one currently at hand) is greater than the
count for the port we're queuing to -- if this condition is
never true, upcall is queued at the tail. This results in a
per-port round-robin order.
In order to implement a fair round-robin behaviour, a variable
queueing delay is introduced. This will be zero if the upcalls
rate is below a given threshold, and grows linearly with the
queue utilisation (i.e. upcalls rate) otherwise.
This ensures fairness among ports under load and with few
netlink sockets.
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Co-authored-by: Stefano Brivio <sbrivio@redhat.com>
---
net/openvswitch/datapath.c | 143 ++++++++++++++++++++++++++++++++++---
net/openvswitch/datapath.h | 27 ++++++-
2 files changed, 161 insertions(+), 9 deletions(-)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 0f5ce77460d4..2cfd504562d8 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -59,6 +59,10 @@
#include "vport-internal_dev.h"
#include "vport-netdev.h"
+#define UPCALL_QUEUE_TIMEOUT msecs_to_jiffies(10)
+#define UPCALL_QUEUE_MAX_DELAY msecs_to_jiffies(10)
+#define UPCALL_QUEUE_MAX_LEN 200
+
unsigned int ovs_net_id __read_mostly;
static struct genl_family dp_packet_genl_family;
@@ -225,6 +229,116 @@ void ovs_dp_detach_port(struct vport *p)
ovs_vport_del(p);
}
+static void ovs_dp_upcall_dequeue(struct work_struct *work)
+{
+ struct datapath *dp = container_of(work, struct datapath,
+ upcalls.work.work);
+ struct dp_upcall_info *u, *n;
+
+ spin_lock_bh(&dp->upcalls.lock);
+ list_for_each_entry_safe(u, n, &dp->upcalls.list, list) {
+ if (unlikely(ovs_dp_upcall(dp, u->skb, &u->key, u, 0)))
+ kfree_skb(u->skb);
+ else
+ consume_skb(u->skb);
+ kfree(u);
+ }
+ dp->upcalls.len = 0;
+ INIT_LIST_HEAD(&dp->upcalls.list);
+ spin_unlock_bh(&dp->upcalls.lock);
+}
+
+/* Calculate the delay of the deferred work which sends the upcalls. If it ran
+ * more than UPCALL_QUEUE_TIMEOUT ago, schedule the work immediately. Otherwise
+ * return a time between 0 and UPCALL_QUEUE_MAX_DELAY, depending linearly on the
+ * queue utilisation.
+ */
+static unsigned long ovs_dp_upcall_delay(int queue_len, unsigned long last_run)
+{
+ if (jiffies - last_run >= UPCALL_QUEUE_TIMEOUT)
+ return 0;
+
+ return UPCALL_QUEUE_MAX_DELAY -
+ UPCALL_QUEUE_MAX_DELAY * queue_len / UPCALL_QUEUE_MAX_LEN;
+}
+
+static int ovs_dp_upcall_queue_roundrobin(struct datapath *dp,
+ struct dp_upcall_info *upcall)
+{
+ struct list_head *head = &dp->upcalls.list;
+ struct dp_upcall_info *here = NULL, *pos;
+ bool find_next = true;
+ unsigned long delay;
+ int err = 0;
+ u8 count;
+
+ spin_lock_bh(&dp->upcalls.lock);
+ if (dp->upcalls.len > UPCALL_QUEUE_MAX_LEN) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ /* Insert upcalls in the list in a per-port round-robin fashion, look
+ * for insertion point:
+ * - to avoid out-of-order per-port upcalls, we can insert only after
+ * the last occurrence of upcalls for the same port
+ * - insert upcall only after we reach a count of occurrences for a
+ * given port greater than the one we're inserting this upcall for
+ */
+ list_for_each_entry(pos, head, list) {
+ /* Count per-port upcalls. */
+ if (dp->upcalls.count[pos->port_no] == U8_MAX - 1) {
+ err = -ENOSPC;
+ goto out_clear;
+ }
+ dp->upcalls.count[pos->port_no]++;
+
+ if (pos->port_no == upcall->port_no) {
+ /* Another upcall for the same port: move insertion
+ * point here, keep looking for insertion condition to
+ * be still met further on.
+ */
+ find_next = true;
+ here = pos;
+ continue;
+ }
+
+ count = dp->upcalls.count[upcall->port_no];
+ if (find_next && dp->upcalls.count[pos->port_no] >= count) {
+ /* Insertion condition met: no need to look further,
+ * unless another upcall for the same port occurs later.
+ */
+ find_next = false;
+ here = pos;
+ }
+ }
+
+ if (here)
+ list_add(&upcall->list, &here->list);
+ else
+ list_add_tail(&upcall->list, head);
+
+ dp->upcalls.len++;
+
+out_clear:
+ /* Clear the per-port counters we used, so that we don't need to zero
+ * out the counters array on every insertion.
+ */
+ list_for_each_entry_reverse(pos, head, list)
+ dp->upcalls.count[pos->port_no] = 0;
+
+out:
+ spin_unlock_bh(&dp->upcalls.lock);
+
+ if (!err) {
+ delay = ovs_dp_upcall_delay(dp->upcalls.len,
+ dp->upcalls.last_run);
+ mod_delayed_work(system_wq, &dp->upcalls.work, delay);
+ }
+
+ return err;
+}
+
/* Must be called with rcu_read_lock. */
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{
@@ -241,18 +355,25 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
/* Look up flow. */
flow = ovs_flow_tbl_lookup_stats(&dp->table, key, &n_mask_hit);
if (unlikely(!flow)) {
- struct dp_upcall_info upcall;
+ struct dp_upcall_info *upcall;
int error;
- memset(&upcall, 0, sizeof(upcall));
- upcall.cmd = OVS_PACKET_CMD_MISS;
- upcall.portid = ovs_vport_find_upcall_portid(p, skb);
- upcall.mru = OVS_CB(skb)->mru;
- error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
+ upcall = kzalloc(sizeof(*upcall), GFP_ATOMIC);
+ if (!upcall) {
+ kfree_skb(skb);
+ stats_counter = &stats->n_missed;
+ goto out;
+ }
+
+ upcall->cmd = OVS_PACKET_CMD_MISS;
+ upcall->portid = ovs_vport_find_upcall_portid(p, skb);
+ upcall->port_no = p->port_no;
+ upcall->mru = OVS_CB(skb)->mru;
+ upcall->skb = skb;
+ upcall->key = *key;
+ error = ovs_dp_upcall_queue_roundrobin(dp, upcall);
if (unlikely(error))
kfree_skb(skb);
- else
- consume_skb(skb);
stats_counter = &stats->n_missed;
goto out;
}
@@ -1589,6 +1710,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++)
INIT_HLIST_HEAD(&dp->ports[i]);
+ INIT_LIST_HEAD(&dp->upcalls.list);
+ spin_lock_init(&dp->upcalls.lock);
+ INIT_DELAYED_WORK(&dp->upcalls.work, ovs_dp_upcall_dequeue);
+
err = ovs_meters_init(dp);
if (err)
goto err_destroy_ports_array;
@@ -1658,6 +1783,8 @@ static void __dp_destroy(struct datapath *dp)
{
int i;
+ cancel_delayed_work_sync(&dp->upcalls.work);
+
for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
struct vport *vport;
struct hlist_node *n;
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index c9eb267c6f7e..f8b8bb679929 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -24,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/workqueue.h>
#include <linux/u64_stats_sync.h>
#include <net/ip_tunnels.h>
@@ -70,6 +71,12 @@ struct dp_stats_percpu {
* @net: Reference to net namespace.
* @max_headroom: the maximum headroom of all vports in this datapath; it will
* be used by all the internal vports in this dp.
+ * @upcalls.work: sends queued upcalls to userspace.
+ * @upcalls.list: list of queued upcalls.
+ * @upcalls.len: elements in upcall_list.
+ * @upcalls.lock: lock for the upcall list.
+ * @upcalls.count: array used to sort the upcalls delivered to userspace.
+ * @upcalls.last_run: timestamp of last work run.
*
* Context: See the comment on locking at the top of datapath.c for additional
* locking information.
@@ -96,6 +103,16 @@ struct datapath {
/* Switch meters. */
struct hlist_head *meters;
+
+ /* Upcalls queue handling. */
+ struct {
+ struct delayed_work work;
+ struct list_head list;
+ int len;
+ spinlock_t lock; /* Protects len and upcall list. */
+ u8 count[DP_MAX_PORTS];
+ unsigned long last_run;
+ } upcalls;
};
/**
@@ -116,7 +133,7 @@ struct ovs_skb_cb {
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
/**
- * struct dp_upcall - metadata to include with a packet to send to userspace
+ * struct dp_upcall_info - Upcall for userspace, including metadata to send
* @cmd: One of %OVS_PACKET_CMD_*.
* @userdata: If nonnull, its variable-length value is passed to userspace as
* %OVS_PACKET_ATTR_USERDATA.
@@ -125,6 +142,10 @@ struct ovs_skb_cb {
* counter.
* @egress_tun_info: If nonnull, becomes %OVS_PACKET_ATTR_EGRESS_TUN_KEY.
* @mru: If not zero, Maximum received IP fragment size.
+ * @list: list within vport for upcall queue handling.
+ * @skb: the socket buffer that generated the upcall.
+ * @key: flow key.
+ * @port_no: port number within the datapath.
*/
struct dp_upcall_info {
struct ip_tunnel_info *egress_tun_info;
@@ -134,6 +155,10 @@ struct dp_upcall_info {
u32 portid;
u8 cmd;
u16 mru;
+ struct list_head list;
+ struct sk_buff *skb;
+ struct sw_flow_key key;
+ u16 port_no;
};
/**
--
2.17.1
^ permalink raw reply related
* [PATCH 2/2] net: qrtr: Reset the node and port ID of broadcast messages
From: Arun Kumar Neelakantam @ 2018-07-04 14:19 UTC (permalink / raw)
To: davem, bjorn.andersson
Cc: netdev, linux-kernel, linux-arm-msm, Arun Kumar Neelakantam,
Florian Westphal, Hannes Frederic Sowa, Denys Vlasenko,
Nicolas Dechesne
In-Reply-To: <1530713973-26696-1-git-send-email-aneela@codeaurora.org>
All the control messages broadcast to remote routers are using
QRTR_NODE_BCAST instead of using local router NODE ID which cause
the packets to be dropped on remote router due to invalid NODE ID.
Signed-off-by: Arun Kumar Neelakantam <aneela@codeaurora.org>
---
net/qrtr/qrtr.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
index 7ffc9a3..86e1e37 100644
--- a/net/qrtr/qrtr.c
+++ b/net/qrtr/qrtr.c
@@ -191,8 +191,13 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
hdr->type = cpu_to_le32(type);
hdr->src_node_id = cpu_to_le32(from->sq_node);
hdr->src_port_id = cpu_to_le32(from->sq_port);
- hdr->dst_node_id = cpu_to_le32(to->sq_node);
- hdr->dst_port_id = cpu_to_le32(to->sq_port);
+ if (to->sq_port == QRTR_PORT_CTRL) {
+ hdr->dst_node_id = cpu_to_le32(node->nid);
+ hdr->dst_port_id = cpu_to_le32(QRTR_NODE_BCAST);
+ } else {
+ hdr->dst_node_id = cpu_to_le32(to->sq_node);
+ hdr->dst_port_id = cpu_to_le32(to->sq_port);
+ }
hdr->size = cpu_to_le32(len);
hdr->confirm_rx = 0;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH 1/2] net: qrtr: Broadcast messages only from control port
From: Arun Kumar Neelakantam @ 2018-07-04 14:19 UTC (permalink / raw)
To: davem, bjorn.andersson
Cc: netdev, linux-kernel, linux-arm-msm, Arun Kumar Neelakantam,
Florian Westphal, Nicolas Dechesne, Denys Vlasenko
In-Reply-To: <1530713973-26696-1-git-send-email-aneela@codeaurora.org>
The broadcast node id should only be sent with the control port id.
Signed-off-by: Arun Kumar Neelakantam <aneela@codeaurora.org>
---
net/qrtr/qrtr.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c
index 2aa07b5..7ffc9a3 100644
--- a/net/qrtr/qrtr.c
+++ b/net/qrtr/qrtr.c
@@ -764,6 +764,10 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
node = NULL;
if (addr->sq_node == QRTR_NODE_BCAST) {
enqueue_fn = qrtr_bcast_enqueue;
+ if (addr->sq_port != QRTR_PORT_CTRL) {
+ release_sock(sk);
+ return -ENOTCONN;
+ }
} else if (addr->sq_node == ipc->us.sq_node) {
enqueue_fn = qrtr_local_enqueue;
} else {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH 0/2] net: qrtr: Broadcasting control messages
From: Arun Kumar Neelakantam @ 2018-07-04 14:19 UTC (permalink / raw)
To: davem, bjorn.andersson
Cc: netdev, linux-kernel, linux-arm-msm, Arun Kumar Neelakantam
Allow messages only from control port to broadcast to avoid unnecessary
messages and reset the node to local router NODE ID in control messages
otherwise remote routers consider the packets as invalid and Drops it.
Arun Kumar Neelakantam (2):
net: qrtr: Broadcast messages only from control port
net: qrtr: Reset the node and port ID of broadcast messages
net/qrtr/qrtr.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [net-next,v2] tcp: Improve setsockopt() TCP_USER_TIMEOUT accuracy
From: Neal Cardwell @ 2018-07-04 14:13 UTC (permalink / raw)
To: Jonathan Maxwell
Cc: David Miller, Eric Dumazet, Alexey Kuznetsov, Hideaki YOSHIFUJI,
Netdev, LKML, jmaxwell
In-Reply-To: <20180704000608.17360-1-jmaxwell37@gmail.com>
On Tue, Jul 3, 2018 at 8:06 PM Jon Maxwell <jmaxwell37@gmail.com> wrote:
>
> Signed-off-by: Jon Maxwell <jmaxwell37@gmail.com>
> ---
> net/ipv4/tcp_timer.c | 48 +++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 39 insertions(+), 9 deletions(-)
>
> diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> index 3b3611729928..d129e670d02a 100644
> --- a/net/ipv4/tcp_timer.c
> +++ b/net/ipv4/tcp_timer.c
> @@ -22,6 +22,39 @@
> #include <linux/gfp.h>
> #include <net/tcp.h>
>
> +unsigned int tcp_retransmit_stamp(struct sock *sk)
> +{
> + unsigned int start_ts = tcp_sk(sk)->retrans_stamp;
Since retrans_stamp and tcp_skb_timestamp() are both u32, I'd suggest
using u32 for the local variable start_ts and the return type of the
function.
> +
> + if (unlikely(!start_ts)) {
> + struct sk_buff *head = tcp_rtx_queue_head(sk);
> +
> + if (!head)
> + return false;
Looks like a copy-and-paste holdover: since this function is returning
an integer, I would suggest returning 0 rather than false.
> + start_ts = tcp_skb_timestamp(head);
> + }
> + return start_ts;
> +}
> +
> +static __u32 tcp_clamp_rto_to_user_timeout(struct sock *sk)
> +{
> + struct inet_connection_sock *icsk = inet_csk(sk);
> + __u32 rto = icsk->icsk_rto;
> + __u32 elapsed, user_timeout;
> + unsigned int start_ts;
I'd suggest u32 here for start_ts (per the rationale above).
> +
> + start_ts = tcp_retransmit_stamp(sk);
> + if (!icsk->icsk_user_timeout || !start_ts)
> + return rto;
> + elapsed = tcp_time_stamp(tcp_sk(sk)) - start_ts;
> + user_timeout = jiffies_to_msecs(icsk->icsk_user_timeout);
> + if (elapsed >= user_timeout)
> + rto = 1; /* user timeout has passed; fire ASAP */
> + else
> + rto = min(rto, (__u32)msecs_to_jiffies(user_timeout - elapsed));
My sense is that min_t would be preferred here, e.g:
rto = min_t(__u32, rto, msecs_to_jiffies(user_timeout - elapsed));
> + return rto;
> +}
> +
> /**
> * tcp_write_err() - close socket and save error info
> * @sk: The socket the error has appeared on.
> @@ -166,14 +199,9 @@ static bool retransmits_timed_out(struct sock *sk,
> if (!inet_csk(sk)->icsk_retransmits)
> return false;
>
> - start_ts = tcp_sk(sk)->retrans_stamp;
> - if (unlikely(!start_ts)) {
> - struct sk_buff *head = tcp_rtx_queue_head(sk);
> -
> - if (!head)
> - return false;
> - start_ts = tcp_skb_timestamp(head);
> - }
> + start_ts = tcp_retransmit_stamp(sk);
> + if (!start_ts)
> + return false;
>
> if (likely(timeout == 0)) {
> linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);
> @@ -407,6 +435,7 @@ void tcp_retransmit_timer(struct sock *sk)
> struct tcp_sock *tp = tcp_sk(sk);
> struct net *net = sock_net(sk);
> struct inet_connection_sock *icsk = inet_csk(sk);
> + __u32 rto;
>
> if (tp->fastopen_rsk) {
> WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
> @@ -535,7 +564,8 @@ void tcp_retransmit_timer(struct sock *sk)
> /* Use normal (exponential) backoff */
> icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
> }
> - inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
> + rto = tcp_clamp_rto_to_user_timeout(sk);
> + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto, TCP_RTO_MAX);
> if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1 + 1, 0))
> __sk_dst_reset(sk);
Thanks!
neal
^ permalink raw reply
* Re: [PATCH net] net: phy: fix flag masking in __set_phy_supported
From: Andrew Lunn @ 2018-07-04 14:13 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: Florian Fainelli, David Miller, netdev@vger.kernel.org
In-Reply-To: <c0ca1da0-2b55-19c0-708a-ccab9a97ec5c@gmail.com>
On Tue, Jul 03, 2018 at 10:34:54PM +0200, Heiner Kallweit wrote:
> Currently also the pause flags are removed from phydev->supported because
> they're not included in PHY_DEFAULT_FEATURES. I don't think this is
> intended, especially when considering that this function can be called
> via phy_set_max_speed() anywhere in a driver. Change the masking to mask
> out only the values we're going to change. In addition remove the
> misleading comment, job of this small function is just to adjust the
> supported and advertised speeds.
>
> Fixes: f3a6bd393c2c ("phylib: Add phy_set_max_speed helper")
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* [PATCH iproute2/net-next] devlink: Add param command support
From: Moshe Shemesh @ 2018-07-04 14:12 UTC (permalink / raw)
To: David S. Miller
Cc: Vasundhara Volam, Jiri Pirko, netdev, linux-kernel, Moshe Shemesh
In-Reply-To: <1530703837-24563-1-git-send-email-moshe@mellanox.com>
Add support for configuration parameters set and show.
Each parameter can be either generic or driver-specific.
The user can retrieve data on these configuration parameters by devlink
param show command and can set new value to a configuration parameter
by devlink param set command.
The configuration parameters can be set in different configuration
modes:
runtime - set while driver is running, no reset required.
driverinit - applied while driver initializes, requires restart
driver by devlink reload command.
permanent - written to device's non-volatile memory, hard reset
required to apply.
New commands added:
devlink dev param show [DEV name PARAMETER]
devlink dev param set DEV name PARAMETER value VALUE
cmode { permanent | driverinit | runtime }
Signed-off-by: Moshe Shemesh <moshe@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
devlink/devlink.c | 454 +++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/devlink.h | 24 +++
man/man8/devlink-dev.8 | 57 ++++++
3 files changed, 535 insertions(+)
diff --git a/devlink/devlink.c b/devlink/devlink.c
index df2c66d..42fa716 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -35,6 +35,10 @@
#define ESWITCH_INLINE_MODE_NETWORK "network"
#define ESWITCH_INLINE_MODE_TRANSPORT "transport"
+#define PARAM_CMODE_RUNTIME_STR "runtime"
+#define PARAM_CMODE_DRIVERINIT_STR "driverinit"
+#define PARAM_CMODE_PERMANENT_STR "permanent"
+
static int g_new_line_count;
#define pr_err(args...) fprintf(stderr, ##args)
@@ -187,6 +191,9 @@ static void ifname_map_free(struct ifname_map *ifname_map)
#define DL_OPT_ESWITCH_ENCAP_MODE BIT(15)
#define DL_OPT_RESOURCE_PATH BIT(16)
#define DL_OPT_RESOURCE_SIZE BIT(17)
+#define DL_OPT_PARAM_NAME BIT(18)
+#define DL_OPT_PARAM_VALUE BIT(19)
+#define DL_OPT_PARAM_CMODE BIT(20)
struct dl_opts {
uint32_t present; /* flags of present items */
@@ -211,6 +218,9 @@ struct dl_opts {
uint32_t resource_size;
uint32_t resource_id;
bool resource_id_valid;
+ const char *param_name;
+ const char *param_value;
+ enum devlink_param_cmode cmode;
};
struct dl {
@@ -348,6 +358,12 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_DPIPE_FIELD_ID] = MNL_TYPE_U32,
[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] = MNL_TYPE_U32,
[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE] = MNL_TYPE_U32,
+ [DEVLINK_ATTR_PARAM] = MNL_TYPE_NESTED,
+ [DEVLINK_ATTR_PARAM_NAME] = MNL_TYPE_STRING,
+ [DEVLINK_ATTR_PARAM_TYPE] = MNL_TYPE_U8,
+ [DEVLINK_ATTR_PARAM_VALUES_LIST] = MNL_TYPE_NESTED,
+ [DEVLINK_ATTR_PARAM_VALUE] = MNL_TYPE_NESTED,
+ [DEVLINK_ATTR_PARAM_VALUE_CMODE] = MNL_TYPE_U8,
};
static int attr_cb(const struct nlattr *attr, void *data)
@@ -514,6 +530,34 @@ static int strtouint16_t(const char *str, uint16_t *p_val)
return 0;
}
+static int strtouint8_t(const char *str, uint8_t *p_val)
+{
+ char *endptr;
+ unsigned long int val;
+
+ val = strtoul(str, &endptr, 10);
+ if (endptr == str || *endptr != '\0')
+ return -EINVAL;
+ if (val > UCHAR_MAX)
+ return -ERANGE;
+ *p_val = val;
+ return 0;
+}
+
+static int strtobool(const char *str, bool *p_val)
+{
+ bool val;
+
+ if (!strcmp(str, "true") || !strcmp(str, "1"))
+ val = true;
+ else if (!strcmp(str, "false") || !strcmp(str, "0"))
+ val = false;
+ else
+ return -EINVAL;
+ *p_val = val;
+ return 0;
+}
+
static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name)
{
strslashrsplit(str, p_bus_name, p_dev_name);
@@ -792,6 +836,22 @@ static int eswitch_encap_mode_get(const char *typestr, bool *p_mode)
return 0;
}
+static int param_cmode_get(const char *cmodestr,
+ enum devlink_param_cmode *cmode)
+{
+ if (strcmp(cmodestr, PARAM_CMODE_RUNTIME_STR) == 0) {
+ *cmode = DEVLINK_PARAM_CMODE_RUNTIME;
+ } else if (strcmp(cmodestr, PARAM_CMODE_DRIVERINIT_STR) == 0) {
+ *cmode = DEVLINK_PARAM_CMODE_DRIVERINIT;
+ } else if (strcmp(cmodestr, PARAM_CMODE_PERMANENT_STR) == 0) {
+ *cmode = DEVLINK_PARAM_CMODE_PERMANENT;
+ } else {
+ pr_err("Unknown configuration mode \"%s\"\n", cmodestr);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int dl_argv_parse(struct dl *dl, uint32_t o_required,
uint32_t o_optional)
{
@@ -973,6 +1033,32 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
if (err)
return err;
o_found |= DL_OPT_RESOURCE_SIZE;
+ } else if (dl_argv_match(dl, "name") &&
+ (o_all & DL_OPT_PARAM_NAME)) {
+ dl_arg_inc(dl);
+ err = dl_argv_str(dl, &opts->param_name);
+ if (err)
+ return err;
+ o_found |= DL_OPT_PARAM_NAME;
+ } else if (dl_argv_match(dl, "value") &&
+ (o_all & DL_OPT_PARAM_VALUE)) {
+ dl_arg_inc(dl);
+ err = dl_argv_str(dl, &opts->param_value);
+ if (err)
+ return err;
+ o_found |= DL_OPT_PARAM_VALUE;
+ } else if (dl_argv_match(dl, "cmode") &&
+ (o_all & DL_OPT_PARAM_CMODE)) {
+ const char *cmodestr;
+
+ dl_arg_inc(dl);
+ err = dl_argv_str(dl, &cmodestr);
+ if (err)
+ return err;
+ err = param_cmode_get(cmodestr, &opts->cmode);
+ if (err)
+ return err;
+ o_found |= DL_OPT_PARAM_CMODE;
} else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL;
@@ -1057,6 +1143,24 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
return -EINVAL;
}
+ if ((o_required & DL_OPT_PARAM_NAME) &&
+ !(o_found & DL_OPT_PARAM_NAME)) {
+ pr_err("Parameter name expected.\n");
+ return -EINVAL;
+ }
+
+ if ((o_required & DL_OPT_PARAM_VALUE) &&
+ !(o_found & DL_OPT_PARAM_VALUE)) {
+ pr_err("Value to set expected.\n");
+ return -EINVAL;
+ }
+
+ if ((o_required & DL_OPT_PARAM_CMODE) &&
+ !(o_found & DL_OPT_PARAM_CMODE)) {
+ pr_err("Configuration mode expected.\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -1121,6 +1225,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
if (opts->present & DL_OPT_RESOURCE_SIZE)
mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE,
opts->resource_size);
+ if (opts->present & DL_OPT_PARAM_NAME)
+ mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_NAME,
+ opts->param_name);
+ if (opts->present & DL_OPT_PARAM_CMODE)
+ mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_CMODE,
+ opts->cmode);
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
@@ -1179,6 +1289,8 @@ static void cmd_dev_help(void)
pr_err(" [ inline-mode { none | link | network | transport } ]\n");
pr_err(" [ encap { disable | enable } ]\n");
pr_err(" devlink dev eswitch show DEV\n");
+ pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
+ pr_err(" devlink dev param show [DEV name PARAMETER]\n");
pr_err(" devlink dev reload DEV\n");
}
@@ -1393,6 +1505,14 @@ static void pr_out_str(struct dl *dl, const char *name, const char *val)
}
}
+static void pr_out_bool(struct dl *dl, const char *name, bool val)
+{
+ if (val)
+ pr_out_str(dl, name, "true");
+ else
+ pr_out_str(dl, name, "false");
+}
+
static void pr_out_uint(struct dl *dl, const char *name, unsigned int val)
{
if (dl->json_output) {
@@ -1475,6 +1595,19 @@ static void pr_out_entry_end(struct dl *dl)
__pr_out_newline();
}
+static const char *param_cmode_name(uint8_t cmode)
+{
+ switch (cmode) {
+ case DEVLINK_PARAM_CMODE_RUNTIME:
+ return PARAM_CMODE_RUNTIME_STR;
+ case DEVLINK_PARAM_CMODE_DRIVERINIT:
+ return PARAM_CMODE_DRIVERINIT_STR;
+ case DEVLINK_PARAM_CMODE_PERMANENT:
+ return PARAM_CMODE_PERMANENT_STR;
+ default: return "<unknown type>";
+ }
+}
+
static const char *eswitch_mode_name(uint32_t mode)
{
switch (mode) {
@@ -1593,6 +1726,304 @@ static int cmd_dev_eswitch(struct dl *dl)
return -ENOENT;
}
+static void pr_out_param_value(struct dl *dl, int nla_type, struct nlattr *nl)
+{
+ struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *val_attr;
+ int err;
+
+ err = mnl_attr_parse_nested(nl, attr_cb, nla_value);
+ if (err != MNL_CB_OK)
+ return;
+
+ if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
+ (nla_type != MNL_TYPE_FLAG &&
+ !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
+ return;
+
+ pr_out_str(dl, "cmode",
+ param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE])));
+ val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
+
+ switch (nla_type) {
+ case MNL_TYPE_U8:
+ pr_out_uint(dl, "value", mnl_attr_get_u8(val_attr));
+ break;
+ case MNL_TYPE_U16:
+ pr_out_uint(dl, "value", mnl_attr_get_u16(val_attr));
+ break;
+ case MNL_TYPE_U32:
+ pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr));
+ break;
+ case MNL_TYPE_STRING:
+ pr_out_str(dl, "value", mnl_attr_get_str(val_attr));
+ break;
+ case MNL_TYPE_FLAG:
+ pr_out_bool(dl, "value", val_attr ? true : false);
+ break;
+ }
+}
+
+static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
+{
+ struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *param_value_attr;
+ int nla_type;
+ int err;
+
+ err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
+ if (err != MNL_CB_OK)
+ return;
+ if (!nla_param[DEVLINK_ATTR_PARAM_NAME] ||
+ !nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
+ !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
+ return;
+
+ if (array)
+ pr_out_handle_start_arr(dl, tb);
+ else
+ __pr_out_handle_start(dl, tb, true, false);
+
+ nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
+
+ pr_out_str(dl, "name",
+ mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]));
+
+ if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC])
+ pr_out_str(dl, "type", "driver-specific");
+ else
+ pr_out_str(dl, "type", "generic");
+
+ pr_out_array_start(dl, "values");
+ mnl_attr_for_each_nested(param_value_attr,
+ nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
+ pr_out_entry_start(dl);
+ pr_out_param_value(dl, nla_type, param_value_attr);
+ pr_out_entry_end(dl);
+ }
+ pr_out_array_end(dl);
+ pr_out_handle_end(dl);
+}
+
+static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+ struct dl *dl = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+ !tb[DEVLINK_ATTR_PARAM])
+ return MNL_CB_ERROR;
+ pr_out_param(dl, tb, true);
+ return MNL_CB_OK;
+}
+
+struct param_ctx {
+ struct dl *dl;
+ int nla_type;
+ union {
+ uint8_t vu8;
+ uint16_t vu16;
+ uint32_t vu32;
+ const char *vstr;
+ bool vbool;
+ } value;
+};
+
+static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *param_value_attr;
+ enum devlink_param_cmode cmode;
+ struct param_ctx *ctx = data;
+ struct dl *dl = ctx->dl;
+ int nla_type;
+ int err;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+ !tb[DEVLINK_ATTR_PARAM])
+ return MNL_CB_ERROR;
+
+ err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
+ if (err != MNL_CB_OK)
+ return MNL_CB_ERROR;
+
+ if (!nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
+ !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
+ return MNL_CB_ERROR;
+
+ nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
+ mnl_attr_for_each_nested(param_value_attr,
+ nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
+ struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *val_attr;
+
+ err = mnl_attr_parse_nested(param_value_attr,
+ attr_cb, nla_value);
+ if (err != MNL_CB_OK)
+ return MNL_CB_ERROR;
+
+ if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
+ (nla_type != MNL_TYPE_FLAG &&
+ !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
+ return MNL_CB_ERROR;
+
+ cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
+ if (cmode == dl->opts.cmode) {
+ val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
+ switch (nla_type) {
+ case MNL_TYPE_U8:
+ ctx->value.vu8 = mnl_attr_get_u8(val_attr);
+ break;
+ case MNL_TYPE_U16:
+ ctx->value.vu16 = mnl_attr_get_u16(val_attr);
+ break;
+ case MNL_TYPE_U32:
+ ctx->value.vu32 = mnl_attr_get_u32(val_attr);
+ break;
+ case MNL_TYPE_STRING:
+ ctx->value.vstr = mnl_attr_get_str(val_attr);
+ break;
+ case MNL_TYPE_FLAG:
+ ctx->value.vbool = val_attr ? true : false;
+ break;
+ }
+ break;
+ }
+ }
+ ctx->nla_type = nla_type;
+ return MNL_CB_OK;
+}
+
+static int cmd_dev_param_set(struct dl *dl)
+{
+ struct param_ctx ctx = {};
+ struct nlmsghdr *nlh;
+ uint32_t val_u32;
+ uint16_t val_u16;
+ uint8_t val_u8;
+ bool val_bool;
+ int err;
+
+ err = dl_argv_parse(dl, DL_OPT_HANDLE |
+ DL_OPT_PARAM_NAME |
+ DL_OPT_PARAM_VALUE |
+ DL_OPT_PARAM_CMODE, 0);
+ if (err)
+ return err;
+
+ /* Get value type */
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ dl_opts_put(nlh, dl);
+
+ ctx.dl = dl;
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_set_cb, &ctx);
+ if (err)
+ return err;
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ dl_opts_put(nlh, dl);
+
+ mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
+ switch (ctx.nla_type) {
+ case MNL_TYPE_U8:
+ err = strtouint8_t(dl->opts.param_value, &val_u8);
+ if (err)
+ goto err_param_value_parse;
+ if (val_u8 == ctx.value.vu8)
+ return 0;
+ mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
+ break;
+ case MNL_TYPE_U16:
+ err = strtouint16_t(dl->opts.param_value, &val_u16);
+ if (err)
+ goto err_param_value_parse;
+ if (val_u16 == ctx.value.vu16)
+ return 0;
+ mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
+ break;
+ case MNL_TYPE_U32:
+ err = strtouint32_t(dl->opts.param_value, &val_u32);
+ if (err)
+ goto err_param_value_parse;
+ if (val_u32 == ctx.value.vu32)
+ return 0;
+ mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
+ break;
+ case MNL_TYPE_FLAG:
+ err = strtobool(dl->opts.param_value, &val_bool);
+ if (err)
+ goto err_param_value_parse;
+ if (val_bool == ctx.value.vbool)
+ return 0;
+ if (val_bool)
+ mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
+ 0, NULL);
+ break;
+ case MNL_TYPE_STRING:
+ mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
+ dl->opts.param_value);
+ if (!strcmp(dl->opts.param_value, ctx.value.vstr))
+ return 0;
+ break;
+ default:
+ printf("Value type not supported\n");
+ return -ENOTSUP;
+ }
+ return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+
+err_param_value_parse:
+ pr_err("Value \"%s\" is not a number or not within range\n",
+ dl->opts.param_value);
+ return err;
+}
+
+static int cmd_dev_param_show(struct dl *dl)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (dl_argc(dl) == 0)
+ flags |= NLM_F_DUMP;
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET, flags);
+
+ if (dl_argc(dl) > 0) {
+ err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE |
+ DL_OPT_PARAM_NAME, 0);
+ if (err)
+ return err;
+ }
+
+ pr_out_section_start(dl, "param");
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_show_cb, dl);
+ pr_out_section_end(dl);
+ return err;
+}
+
+static int cmd_dev_param(struct dl *dl)
+{
+ if (dl_argv_match(dl, "help")) {
+ cmd_dev_help();
+ return 0;
+ } else if (dl_argv_match(dl, "show") ||
+ dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+ dl_arg_inc(dl);
+ return cmd_dev_param_show(dl);
+ } else if (dl_argv_match(dl, "set")) {
+ dl_arg_inc(dl);
+ return cmd_dev_param_set(dl);
+ }
+ pr_err("Command \"%s\" not found\n", dl_argv(dl));
+ return -ENOENT;
+}
static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct dl *dl = data;
@@ -1669,6 +2100,9 @@ static int cmd_dev(struct dl *dl)
} else if (dl_argv_match(dl, "reload")) {
dl_arg_inc(dl);
return cmd_dev_reload(dl);
+ } else if (dl_argv_match(dl, "param")) {
+ dl_arg_inc(dl);
+ return cmd_dev_param(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
@@ -2632,6 +3066,10 @@ static const char *cmd_name(uint8_t cmd)
case DEVLINK_CMD_PORT_SET: return "set";
case DEVLINK_CMD_PORT_NEW: return "new";
case DEVLINK_CMD_PORT_DEL: return "del";
+ case DEVLINK_CMD_PARAM_GET: return "get";
+ case DEVLINK_CMD_PARAM_SET: return "set";
+ case DEVLINK_CMD_PARAM_NEW: return "new";
+ case DEVLINK_CMD_PARAM_DEL: return "del";
default: return "<unknown cmd>";
}
}
@@ -2650,6 +3088,11 @@ static const char *cmd_obj(uint8_t cmd)
case DEVLINK_CMD_PORT_NEW:
case DEVLINK_CMD_PORT_DEL:
return "port";
+ case DEVLINK_CMD_PARAM_GET:
+ case DEVLINK_CMD_PARAM_SET:
+ case DEVLINK_CMD_PARAM_NEW:
+ case DEVLINK_CMD_PARAM_DEL:
+ return "param";
default: return "<unknown obj>";
}
}
@@ -2706,6 +3149,17 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
pr_out_mon_header(genl->cmd);
pr_out_port(dl, tb);
break;
+ case DEVLINK_CMD_PARAM_GET: /* fall through */
+ case DEVLINK_CMD_PARAM_SET: /* fall through */
+ case DEVLINK_CMD_PARAM_NEW: /* fall through */
+ case DEVLINK_CMD_PARAM_DEL:
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+ !tb[DEVLINK_ATTR_PARAM])
+ return MNL_CB_ERROR;
+ pr_out_mon_header(genl->cmd);
+ pr_out_param(dl, tb, false);
+ break;
}
return MNL_CB_OK;
}
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 493f71f..f7fadd7 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -78,6 +78,11 @@ enum devlink_command {
*/
DEVLINK_CMD_RELOAD,
+ DEVLINK_CMD_PARAM_GET, /* can dump */
+ DEVLINK_CMD_PARAM_SET,
+ DEVLINK_CMD_PARAM_NEW,
+ DEVLINK_CMD_PARAM_DEL,
+
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@@ -142,6 +147,16 @@ enum devlink_port_flavour {
*/
};
+enum devlink_param_cmode {
+ DEVLINK_PARAM_CMODE_RUNTIME,
+ DEVLINK_PARAM_CMODE_DRIVERINIT,
+ DEVLINK_PARAM_CMODE_PERMANENT,
+
+ /* Add new configuration modes above */
+ __DEVLINK_PARAM_CMODE_MAX,
+ DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
+};
+
enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC,
@@ -238,6 +253,15 @@ enum devlink_attr {
DEVLINK_ATTR_PORT_NUMBER, /* u32 */
DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER, /* u32 */
+ DEVLINK_ATTR_PARAM, /* nested */
+ DEVLINK_ATTR_PARAM_NAME, /* string */
+ DEVLINK_ATTR_PARAM_GENERIC, /* flag */
+ DEVLINK_ATTR_PARAM_TYPE, /* u8 */
+ DEVLINK_ATTR_PARAM_VALUES_LIST, /* nested */
+ DEVLINK_ATTR_PARAM_VALUE, /* nested */
+ DEVLINK_ATTR_PARAM_VALUE_DATA, /* dynamic */
+ DEVLINK_ATTR_PARAM_VALUE_CMODE, /* u8 */
+
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8
index 7c749dd..d985da1 100644
--- a/man/man8/devlink-dev.8
+++ b/man/man8/devlink-dev.8
@@ -43,6 +43,23 @@ devlink-dev \- devlink device configuration
.IR DEV
.ti -8
+.BR "devlink dev param set"
+.IR DEV
+.BR name
+.IR PARAMETER
+.BR value
+.IR VALUE
+.BR cmode " { " runtime " | " driverinit " | " permanent " } "
+
+.ti -8
+.BR "devlink dev param show"
+.RI "[ "
+.IR DEV
+.BR name
+.IR PARAMETER
+.RI "]"
+
+.ti -8
.BR "devlink dev reload"
.IR DEV
@@ -98,6 +115,36 @@ Set eswitch encapsulation support
.I enable
- Enable encapsulation support
+.SS devlink dev param set - set new value to devlink device configuration parameter
+
+.TP
+.BI name " PARAMETER"
+Specify parameter name to set.
+
+.TP
+.BI value " VALUE"
+New value to set.
+
+.TP
+.BR cmode " { " runtime " | " driverinit " | " permanent " } "
+Configuration mode in which the new value is set.
+
+.I runtime
+- Set new value while driver is running. This configuration mode doesn't require any reset to apply the new value.
+
+.I driverinit
+- Set new value which will be applied during driver initialization. This configuration mode requires restart driver by devlink reload command to apply the new value.
+
+.I permanent
+- New value is written to device's non-volatile memory. This configuration mode requires hard reset to apply the new value.
+
+.SS devlink dev param show - display devlink device supported configuration parameters attributes
+
+.BR name
+.IR PARAMETER
+Specify parameter name to show.
+If this argument is omitted all parameters supported by devlink devices are listed.
+
.SS devlink dev reload - perform hot reload of the driver.
.PP
@@ -126,6 +173,16 @@ devlink dev eswitch set pci/0000:01:00.0 mode switchdev
Sets the eswitch mode of specified devlink device to switchdev.
.RE
.PP
+devlink dev param show pci/0000:01:00.0 name max_macs
+.RS 4
+Shows the parameter max_macs attributes.
+.RE
+.PP
+devlink dev param set pci/0000:01:00.0 name internal_error_reset value true cmode runtime
+.RS 4
+Sets the parameter internal_error_reset of specified devlink device to true.
+.RE
+.PP
devlink dev reload pci/0000:01:00.0
.RS 4
Performs hot reload of specified devlink device.
--
1.8.3.1
^ permalink raw reply related
* [PATCH net] qed: Fix reading stale configuration information
From: Denis Bolotin @ 2018-07-04 14:06 UTC (permalink / raw)
To: davem, netdev; +Cc: Denis Bolotin, Ariel Elior
Configuration information read at driver load can become stale after it is
updated. Mark information as not valid and re-populate when this happens.
Signed-off-by: Denis Bolotin <denis.bolotin@cavium.com>
Signed-off-by: Ariel Elior <ariel.elior@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 39 +++++++++++++++++++++----------
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 00db340..1dfaccd 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -502,6 +502,7 @@ enum BAR_ID {
struct qed_nvm_image_info {
u32 num_images;
struct bist_nvm_image_att *image_att;
+ bool valid;
};
#define DRV_MODULE_VERSION \
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 4e0b443..9d9e533 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -592,6 +592,9 @@ int qed_mcp_nvm_wr_cmd(struct qed_hwfn *p_hwfn,
*o_mcp_resp = mb_params.mcp_resp;
*o_mcp_param = mb_params.mcp_param;
+ /* nvm_info needs to be updated */
+ p_hwfn->nvm_info.valid = false;
+
return 0;
}
@@ -2555,11 +2558,14 @@ int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn)
{
- struct qed_nvm_image_info *nvm_info = &p_hwfn->nvm_info;
+ struct qed_nvm_image_info nvm_info;
struct qed_ptt *p_ptt;
int rc;
u32 i;
+ if (p_hwfn->nvm_info.valid)
+ return 0;
+
p_ptt = qed_ptt_acquire(p_hwfn);
if (!p_ptt) {
DP_ERR(p_hwfn, "failed to acquire ptt\n");
@@ -2567,29 +2573,29 @@ int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn)
}
/* Acquire from MFW the amount of available images */
- nvm_info->num_images = 0;
+ nvm_info.num_images = 0;
rc = qed_mcp_bist_nvm_get_num_images(p_hwfn,
- p_ptt, &nvm_info->num_images);
+ p_ptt, &nvm_info.num_images);
if (rc == -EOPNOTSUPP) {
DP_INFO(p_hwfn, "DRV_MSG_CODE_BIST_TEST is not supported\n");
goto out;
- } else if (rc || !nvm_info->num_images) {
+ } else if (rc || !nvm_info.num_images) {
DP_ERR(p_hwfn, "Failed getting number of images\n");
goto err0;
}
- nvm_info->image_att = kmalloc_array(nvm_info->num_images,
- sizeof(struct bist_nvm_image_att),
- GFP_KERNEL);
- if (!nvm_info->image_att) {
+ nvm_info.image_att = kmalloc_array(nvm_info.num_images,
+ sizeof(struct bist_nvm_image_att),
+ GFP_KERNEL);
+ if (!nvm_info.image_att) {
rc = -ENOMEM;
goto err0;
}
/* Iterate over images and get their attributes */
- for (i = 0; i < nvm_info->num_images; i++) {
+ for (i = 0; i < nvm_info.num_images; i++) {
rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
- &nvm_info->image_att[i], i);
+ &nvm_info.image_att[i], i);
if (rc) {
DP_ERR(p_hwfn,
"Failed getting image index %d attributes\n", i);
@@ -2597,14 +2603,22 @@ int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn)
}
DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n", i,
- nvm_info->image_att[i].len);
+ nvm_info.image_att[i].len);
}
out:
+ /* Update hwfn's nvm_info */
+ if (nvm_info.num_images) {
+ p_hwfn->nvm_info.num_images = nvm_info.num_images;
+ kfree(p_hwfn->nvm_info.image_att);
+ p_hwfn->nvm_info.image_att = nvm_info.image_att;
+ p_hwfn->nvm_info.valid = true;
+ }
+
qed_ptt_release(p_hwfn, p_ptt);
return 0;
err1:
- kfree(nvm_info->image_att);
+ kfree(nvm_info.image_att);
err0:
qed_ptt_release(p_hwfn, p_ptt);
return rc;
@@ -2641,6 +2655,7 @@ int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn)
return -EINVAL;
}
+ qed_mcp_nvm_info_populate(p_hwfn);
for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
if (type == p_hwfn->nvm_info.image_att[i].image_type)
break;
--
1.8.3.1
^ permalink raw reply related
* [RFC PATCH v3] ipv6: make ipv6_renew_options() interrupt/kernel safe
From: Paul Moore @ 2018-07-04 13:58 UTC (permalink / raw)
To: netdev; +Cc: viro, selinux, linux-security-module
From: Paul Moore <paul@paul-moore.com>
At present the ipv6_renew_options_kern() function ends up calling into
access_ok() which is problematic if done from inside an interrupt as
access_ok() calls WARN_ON_IN_IRQ() on some (all?) architectures
(x86-64 is affected). Example warning/backtrace is shown below:
WARNING: CPU: 1 PID: 3144 at lib/usercopy.c:11 _copy_from_user+0x85/0x90
...
Call Trace:
<IRQ>
ipv6_renew_option+0xb2/0xf0
ipv6_renew_options+0x26a/0x340
ipv6_renew_options_kern+0x2c/0x40
calipso_req_setattr+0x72/0xe0
netlbl_req_setattr+0x126/0x1b0
selinux_netlbl_inet_conn_request+0x80/0x100
selinux_inet_conn_request+0x6d/0xb0
security_inet_conn_request+0x32/0x50
tcp_conn_request+0x35f/0xe00
? __lock_acquire+0x250/0x16c0
? selinux_socket_sock_rcv_skb+0x1ae/0x210
? tcp_rcv_state_process+0x289/0x106b
tcp_rcv_state_process+0x289/0x106b
? tcp_v6_do_rcv+0x1a7/0x3c0
tcp_v6_do_rcv+0x1a7/0x3c0
tcp_v6_rcv+0xc82/0xcf0
ip6_input_finish+0x10d/0x690
ip6_input+0x45/0x1e0
? ip6_rcv_finish+0x1d0/0x1d0
ipv6_rcv+0x32b/0x880
? ip6_make_skb+0x1e0/0x1e0
__netif_receive_skb_core+0x6f2/0xdf0
? process_backlog+0x85/0x250
? process_backlog+0x85/0x250
? process_backlog+0xec/0x250
process_backlog+0xec/0x250
net_rx_action+0x153/0x480
__do_softirq+0xd9/0x4f7
do_softirq_own_stack+0x2a/0x40
</IRQ>
...
While not present in the backtrace, ipv6_renew_option() ends up calling
access_ok() via the following chain:
access_ok()
_copy_from_user()
copy_from_user()
ipv6_renew_option()
The fix presented in this patch is to perform the userspace copy
earlier in the call chain such that it is only called when the option
data is actually coming from userspace; that place is
do_ipv6_setsockopt(). Not only does this solve the problem seen in
the backtrace above, it also allows us to simplify the code quite a
bit by removing ipv6_renew_options_kern() completely. We also take
this opportunity to cleanup ipv6_renew_options()/ipv6_renew_option()
a small amount as well.
This patch is heavily based on a rough patch by Al Viro. I've taken
his original patch, converted a kmemdup() call in do_ipv6_setsockopt()
to a memdup_user() call, made better use of the e_inval jump target in
the same function, and cleaned up the use ipv6_renew_option() by
ipv6_renew_options().
CC: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Paul Moore <paul@paul-moore.com>
--
v3:
- fix typo in ipv6_renew_option() spotted by David Miller
v2:
- handle opt == NULL properly in ipv6_renew_options()
---
include/net/ipv6.h | 9 ----
net/ipv6/calipso.c | 9 +---
net/ipv6/exthdrs.c | 111 ++++++++++++----------------------------------
net/ipv6/ipv6_sockglue.c | 27 ++++++++---
4 files changed, 53 insertions(+), 103 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 16475c269749..d02881e4ad1f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -355,14 +355,7 @@ struct ipv6_txoptions *ipv6_dup_options(struct sock *sk,
struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_txoptions *opt,
int newtype,
- struct ipv6_opt_hdr __user *newopt,
- int newoptlen);
-struct ipv6_txoptions *
-ipv6_renew_options_kern(struct sock *sk,
- struct ipv6_txoptions *opt,
- int newtype,
- struct ipv6_opt_hdr *newopt,
- int newoptlen);
+ struct ipv6_opt_hdr *newopt);
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c
index 1323b9679cf7..1c0bb9fb76e6 100644
--- a/net/ipv6/calipso.c
+++ b/net/ipv6/calipso.c
@@ -799,8 +799,7 @@ static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop)
{
struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts;
- txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS,
- hop, hop ? ipv6_optlen(hop) : 0);
+ txopts = ipv6_renew_options(sk, old, IPV6_HOPOPTS, hop);
txopt_put(old);
if (IS_ERR(txopts))
return PTR_ERR(txopts);
@@ -1222,8 +1221,7 @@ static int calipso_req_setattr(struct request_sock *req,
if (IS_ERR(new))
return PTR_ERR(new);
- txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS,
- new, new ? ipv6_optlen(new) : 0);
+ txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
kfree(new);
@@ -1260,8 +1258,7 @@ static void calipso_req_delattr(struct request_sock *req)
if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new))
return; /* Nothing to do */
- txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS,
- new, new ? ipv6_optlen(new) : 0);
+ txopts = ipv6_renew_options(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, new);
if (!IS_ERR(txopts)) {
txopts = xchg(&req_inet->ipv6_opt, txopts);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 5bc2bf3733ab..20291c2036fc 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -1015,29 +1015,21 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
}
EXPORT_SYMBOL_GPL(ipv6_dup_options);
-static int ipv6_renew_option(void *ohdr,
- struct ipv6_opt_hdr __user *newopt, int newoptlen,
- int inherit,
- struct ipv6_opt_hdr **hdr,
- char **p)
+static void ipv6_renew_option(int renewtype,
+ struct ipv6_opt_hdr **dest,
+ struct ipv6_opt_hdr *old,
+ struct ipv6_opt_hdr *new,
+ int newtype, char **p)
{
- if (inherit) {
- if (ohdr) {
- memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
- *hdr = (struct ipv6_opt_hdr *)*p;
- *p += CMSG_ALIGN(ipv6_optlen(*hdr));
- }
- } else {
- if (newopt) {
- if (copy_from_user(*p, newopt, newoptlen))
- return -EFAULT;
- *hdr = (struct ipv6_opt_hdr *)*p;
- if (ipv6_optlen(*hdr) > newoptlen)
- return -EINVAL;
- *p += CMSG_ALIGN(newoptlen);
- }
- }
- return 0;
+ struct ipv6_opt_hdr *src;
+
+ src = (renewtype == newtype ? new : old);
+ if (!src)
+ return;
+
+ memcpy(*p, src, ipv6_optlen(src));
+ *dest = (struct ipv6_opt_hdr *)*p;
+ *p += CMSG_ALIGN(ipv6_optlen(*dest));
}
/**
@@ -1063,13 +1055,11 @@ static int ipv6_renew_option(void *ohdr,
*/
struct ipv6_txoptions *
ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
- int newtype,
- struct ipv6_opt_hdr __user *newopt, int newoptlen)
+ int newtype, struct ipv6_opt_hdr *newopt)
{
int tot_len = 0;
char *p;
struct ipv6_txoptions *opt2;
- int err;
if (opt) {
if (newtype != IPV6_HOPOPTS && opt->hopopt)
@@ -1082,8 +1072,8 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
}
- if (newopt && newoptlen)
- tot_len += CMSG_ALIGN(newoptlen);
+ if (newopt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
if (!tot_len)
return NULL;
@@ -1098,29 +1088,19 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
opt2->tot_len = tot_len;
p = (char *)(opt2 + 1);
- err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
- newtype != IPV6_HOPOPTS,
- &opt2->hopopt, &p);
- if (err)
- goto out;
-
- err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
- newtype != IPV6_RTHDRDSTOPTS,
- &opt2->dst0opt, &p);
- if (err)
- goto out;
-
- err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
- newtype != IPV6_RTHDR,
- (struct ipv6_opt_hdr **)&opt2->srcrt, &p);
- if (err)
- goto out;
-
- err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
- newtype != IPV6_DSTOPTS,
- &opt2->dst1opt, &p);
- if (err)
- goto out;
+ ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
+ (opt ? opt->hopopt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
+ (opt ? opt->dst0opt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_RTHDR,
+ (struct ipv6_opt_hdr **)&opt2->srcrt,
+ (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
+ (opt ? opt->dst1opt : NULL),
+ newopt, newtype, &p);
opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
(opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
@@ -1128,37 +1108,6 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
return opt2;
-out:
- sock_kfree_s(sk, opt2, opt2->tot_len);
- return ERR_PTR(err);
-}
-
-/**
- * ipv6_renew_options_kern - replace a specific ext hdr with a new one.
- *
- * @sk: sock from which to allocate memory
- * @opt: original options
- * @newtype: option type to replace in @opt
- * @newopt: new option of type @newtype to replace (kernel-mem)
- * @newoptlen: length of @newopt
- *
- * See ipv6_renew_options(). The difference is that @newopt is
- * kernel memory, rather than user memory.
- */
-struct ipv6_txoptions *
-ipv6_renew_options_kern(struct sock *sk, struct ipv6_txoptions *opt,
- int newtype, struct ipv6_opt_hdr *newopt,
- int newoptlen)
-{
- struct ipv6_txoptions *ret_val;
- const mm_segment_t old_fs = get_fs();
-
- set_fs(KERNEL_DS);
- ret_val = ipv6_renew_options(sk, opt, newtype,
- (struct ipv6_opt_hdr __user *)newopt,
- newoptlen);
- set_fs(old_fs);
- return ret_val;
}
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 4d780c7f0130..c95c3486d904 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -398,6 +398,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
case IPV6_DSTOPTS:
{
struct ipv6_txoptions *opt;
+ struct ipv6_opt_hdr *new = NULL;
+
+ /* hop-by-hop / destination options are privileged option */
+ retv = -EPERM;
+ if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
+ break;
/* remove any sticky options header with a zero option
* length, per RFC3542.
@@ -409,17 +415,22 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
else if (optlen < sizeof(struct ipv6_opt_hdr) ||
optlen & 0x7 || optlen > 8 * 255)
goto e_inval;
-
- /* hop-by-hop / destination options are privileged option */
- retv = -EPERM;
- if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
- break;
+ else {
+ new = memdup_user(optval, optlen);
+ if (IS_ERR(new)) {
+ retv = PTR_ERR(new);
+ break;
+ }
+ if (unlikely(ipv6_optlen(new) > optlen)) {
+ kfree(new);
+ goto e_inval;
+ }
+ }
opt = rcu_dereference_protected(np->opt,
lockdep_sock_is_held(sk));
- opt = ipv6_renew_options(sk, opt, optname,
- (struct ipv6_opt_hdr __user *)optval,
- optlen);
+ opt = ipv6_renew_options(sk, opt, optname, new);
+ kfree(new);
if (IS_ERR(opt)) {
retv = PTR_ERR(opt);
break;
^ permalink raw reply related
* RE: [RFC net-next 15/15] net: lora: Add Semtech SX1301
From: Ben Whitten @ 2018-07-04 13:41 UTC (permalink / raw)
To: Mark Brown, Andreas Färber
Cc: Steve deRosier, Matthias Brugger, Jiri Pirko, Hasnain Virk,
netdev@vger.kernel.org, Marcel Holtmann, Dollar Chen,
linux-kernel@vger.kernel.org, Michael Röder, Janus Piwek,
linux-spi@vger.kernel.org, LoRa_Community_Support@semtech.com,
Jian-Hong Pan, Ken Yu, David S . Miller,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20180704114355.GB11693@sirena.org.uk>
> Subject: Re: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> On Tue, Jul 03, 2018 at 06:40:41PM +0200, Andreas Färber wrote:
>
> > Do you have an alternative solution for abstraction? A regmap would seem
> > to require putting everything into a monolithic SX1301 driver despite
> > those connected chipsets actually being regular, external SPI chips that
> > could also be attached to non-SX1301 SPI masters.
>
> I'm suggesting a regmap for those external SPI chips. It doesn't matter
> what the host uses.
Currently in my testing drivers I have used regmap for both the SX1301 and the downstream SX1257.
In my SX1257 driver currently the regmap is backed via SPI with devm_regmap_init_spi, please correct me if I am wrong but if I understand correctly this could be split out to regmap backed by SPI in the case of a direct host connection, and a regmap backed by the SX1301's regmapped strobing register interface, regmap_bus?
Is there a precedent to this I can examine?
^ permalink raw reply
* Re: [PATCH] epic100: remove redundant variable 'irq'
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king; +Cc: allen.lkml, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704121929.9113-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 13:19:29 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable 'irq' is being assigned but is never used hence it is
> and can be removed.
>
> Cleans up clang warning:
> warning: variable 'irq' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] sfc: remove redundant variable old_vlan
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king
Cc: linux-net-drivers, ecree, bkenward, netdev, kernel-janitors,
linux-kernel
In-Reply-To: <20180704121301.8896-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 13:13:01 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable old_vlan is being assigned but is never used hence it is
> and can be removed.
>
> Cleans up clang warning:
> warning: variable 'old_vlan' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] qed: remove redundant pointer 'name'
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king
Cc: Ariel.Elior, everest-linux-l2, netdev, kernel-janitors,
linux-kernel
In-Reply-To: <20180704120626.8701-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 13:06:26 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer 'name' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'name' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] qlogic: netxen: remove various redundant variables
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king
Cc: manish.chopra, rahul.verma, Dept-GELinuxNICDev, netdev,
kernel-janitors, linux-kernel
In-Reply-To: <20180704114553.8293-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 12:45:53 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variables consumer, cmd_desc, end_cnt and no_of_desc are being assigned
> but are never used hence they are redundant and can be removed.
>
> Cleans up clang warnings:
> warning: variable 'consumer' set but not used [-Wunused-but-set-variable]
> warning: variable 'cmd_desc' set but not used [-Wunused-but-set-variable]
> warning: variable 'end_cnt' set but not used [-Wunused-but-set-variable]
> warning: variable 'no_of_desc' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] ethernet: micrel: remove redundant pointer 'info'
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king; +Cc: netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704112044.7930-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 12:20:44 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer 'info' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'info' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: hinic: remove redundant pointer pfhwdev
From: David Miller @ 2018-07-04 13:41 UTC (permalink / raw)
To: colin.king; +Cc: aviad.krawczyk, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704080627.16393-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 09:06:27 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer pfhwdev is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'pfhwdev' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: hns3: remove redundant variable 'protocol'
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king
Cc: yisen.zhuang, salil.mehta, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704075925.16163-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:59:25 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable 'protocol' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'protocol' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: ethernet: gianfar_ethtool: remove redundant variable last_rule_idx
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king; +Cc: claudiu.manoil, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704075455.16010-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:54:55 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable last_rule_idx is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'last_rule_idx' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: fec: remove redundant variable 'inc'
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king; +Cc: fugang.duan, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704074943.15820-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:49:43 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable 'inc' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'inc' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] cnic: remove redundant pointer req and variable func
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king; +Cc: christophe.jaillet, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704073912.15598-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:39:12 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer req and variable func are being assigned but are never used
> hence they are redundant and can be removed.
>
> Cleans up clang warnings:
> warning: variable 'req' set but not used [-Wunused-but-set-variable]
> warning: variable 'func' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: bgmac: remove redundant variable 'freed'
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king
Cc: f.fainelli, scott.branden, abhishek.shah, netdev, kernel-janitors,
linux-kernel
In-Reply-To: <20180704073043.15286-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:30:43 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable 'freed' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'freed' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: ethernet: nb8800: remove redundant pointer rxd
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king; +Cc: marc_gonzalez, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704072020.14785-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:20:20 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer rxd is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'rxb' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: alx: remove redundant variable old_duplex
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king
Cc: jcliburn, chris.snook, sd, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704071530.14431-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:15:30 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable old_duplex is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'old_duplex' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: alteon: acenic: remove redundant pointer rxdesc
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king; +Cc: jes, linux-acenic, netdev, kernel-janitors, linux-kernel
In-Reply-To: <20180704070135.13938-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 08:01:35 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Pointer rxdesc is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'rxdesc' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* Re: [PATCH] net: dsa: bcm_sf2: remove redundant variable off
From: David Miller @ 2018-07-04 13:40 UTC (permalink / raw)
To: colin.king
Cc: andrew, vivien.didelot, f.fainelli, netdev, kernel-janitors,
linux-kernel
In-Reply-To: <20180704065436.13719-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Wed, 4 Jul 2018 07:54:36 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Variable 'off' is being assigned but is never used hence it is
> redundant and can be removed.
>
> Cleans up clang warning:
> warning: variable 'off' set but not used [-Wunused-but-set-variable]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox