* Re: [PATCH net-next 2/2] pfifo_fast: drop unneeded additional lock on dequeue
From: Michael S. Tsirkin @ 2018-05-16 14:24 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, David S. Miller, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
John Fastabend
In-Reply-To: <1526457376.2553.1.camel@redhat.com>
On Wed, May 16, 2018 at 09:56:16AM +0200, Paolo Abeni wrote:
> On Tue, 2018-05-15 at 23:17 +0300, Michael S. Tsirkin wrote:
> > On Tue, May 15, 2018 at 04:24:37PM +0200, Paolo Abeni wrote:
> > > After the previous patch, for NOLOCK qdiscs, q->seqlock is
> > > always held when the dequeue() is invoked, we can drop
> > > any additional locking to protect such operation.
> > >
> > > Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> > > ---
> > > include/linux/skb_array.h | 5 +++++
> > > net/sched/sch_generic.c | 4 ++--
> > > 2 files changed, 7 insertions(+), 2 deletions(-)
> >
> > Is the seqlock taken during qdisc_change_tx_queue_len?
> > We need to prevent that racing with dequeue.
>
> Thanks for the head-up! I missed that code-path.
>
> I'll add the lock in qdisc_change_tx_queue_len() in v2.
>
> Thanks you,
>
> Paolo
When you do, make sure locks nest consistently.
^ permalink raw reply
* Re: Xilinx axienet + DP83620 in fiber mode won't set netif_carrier_on
From: Alvaro G. M. @ 2018-05-16 14:24 UTC (permalink / raw)
To: Andrew Lunn; +Cc: netdev
In-Reply-To: <20180516131134.GF22000@lunn.ch>
On Wed, May 16, 2018 at 03:11:34PM +0200, Andrew Lunn wrote:
> Hi Alvaro
>
> What should happen in general terms is that at some point the link to
> the peer is established. phylib, the generic PHY code, polls the PHY
> ever second, asking what the link state is. When the link changes from
> down to up, phylib will call the link_adjust callback in the MAC, and
> netif_carrier_on().
>
> When the PHY reports the link has gone down, it does similar, calls
> the adjust_link callback, and netif_carrier_off().
>
> So what you need to do is find out why the PHY driver never reports
> link up. Does the PHY even know when the link is up? Often SFF/SFP
> modules have a Signal Detect pin, which is connected to a gpio. Do you
> have something like that? If so, you should look at the PHYLINK code
> and the SFP device which was added recently.
I didn't know about the SFP device. I don't think this will help my specific
case because my board didn't route the i2c bus from the SFP, so it basically
sits there and does it thing alone, I can't communicate with it.
I see that net/phy/marvell.c has a custom marvell_update_link that reads a
different register to check for fiber connectivity instead of using
genphy_update_link, which I see reads from MII_BMSR.BMSR_LSTATUS
It looks like the DP83620 may do something similar, and the fiber status may
be accesible from some other register. This starts to make sense, thanks for
setting my on track!
Best regards!
--
Alvaro G. M.
^ permalink raw reply
* [PATCH net-next] cxgb4: update LE-TCAM collection for T6
From: Rahul Lakkireddy @ 2018-05-16 14:21 UTC (permalink / raw)
To: netdev; +Cc: davem, ganeshgr, nirranjan, indranil, Rahul Lakkireddy
For T6, clip table is separated from main TCAM. So, update LE-TCAM
collection logic to collect clip table TCAM as well. IPv6 takes
4 entries in clip table TCAM compared to 2 entries in main TCAM.
Also, in case of errors, keep LE-TCAM collected so far and set the
status to partial dump.
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
---
drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h | 3 ++
drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h | 1 +
drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c | 40 ++++++++++++++++++-----
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 1 +
4 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h
index b57acb8dc35b..740a18ba4229 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h
@@ -235,6 +235,9 @@ struct cudbg_vpd_data {
};
#define CUDBG_MAX_TCAM_TID 0x800
+#define CUDBG_T6_CLIP 1536
+#define CUDBG_MAX_TID_COMP_EN 6144
+#define CUDBG_MAX_TID_COMP_DIS 3072
enum cudbg_le_entry_types {
LE_ET_UNKNOWN = 0,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h
index 8568a51f6414..215fe6260fd7 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h
@@ -24,6 +24,7 @@
#define CUDBG_STATUS_NOT_IMPLEMENTED -28
#define CUDBG_SYSTEM_ERROR -29
#define CUDBG_STATUS_CCLK_NOT_DEFINED -32
+#define CUDBG_STATUS_PARTIAL_DATA -41
#define CUDBG_MAJOR_VERSION 1
#define CUDBG_MINOR_VERSION 14
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
index 9da6f57901a9..4feb7eca0acf 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
@@ -2366,8 +2366,11 @@ void cudbg_fill_le_tcam_info(struct adapter *padap,
value = t4_read_reg(padap, LE_DB_ROUTING_TABLE_INDEX_A);
tcam_region->routing_start = value;
- /*Get clip table index */
- value = t4_read_reg(padap, LE_DB_CLIP_TABLE_INDEX_A);
+ /* Get clip table index. For T6 there is separate CLIP TCAM */
+ if (is_t6(padap->params.chip))
+ value = t4_read_reg(padap, LE_DB_CLCAM_TID_BASE_A);
+ else
+ value = t4_read_reg(padap, LE_DB_CLIP_TABLE_INDEX_A);
tcam_region->clip_start = value;
/* Get filter table index */
@@ -2392,8 +2395,16 @@ void cudbg_fill_le_tcam_info(struct adapter *padap,
tcam_region->tid_hash_base;
}
} else { /* hash not enabled */
- tcam_region->max_tid = CUDBG_MAX_TCAM_TID;
+ if (is_t6(padap->params.chip))
+ tcam_region->max_tid = (value & ASLIPCOMPEN_F) ?
+ CUDBG_MAX_TID_COMP_EN :
+ CUDBG_MAX_TID_COMP_DIS;
+ else
+ tcam_region->max_tid = CUDBG_MAX_TCAM_TID;
}
+
+ if (is_t6(padap->params.chip))
+ tcam_region->max_tid += CUDBG_T6_CLIP;
}
int cudbg_collect_le_tcam(struct cudbg_init *pdbg_init,
@@ -2423,18 +2434,31 @@ int cudbg_collect_le_tcam(struct cudbg_init *pdbg_init,
for (i = 0; i < tcam_region.max_tid; ) {
rc = cudbg_read_tid(pdbg_init, i, tid_data);
if (rc) {
- cudbg_err->sys_err = rc;
- cudbg_put_buff(pdbg_init, &temp_buff);
- return rc;
+ cudbg_err->sys_warn = CUDBG_STATUS_PARTIAL_DATA;
+ /* Update tcam header and exit */
+ tcam_region.max_tid = i;
+ memcpy(temp_buff.data, &tcam_region,
+ sizeof(struct cudbg_tcam));
+ goto out;
}
- /* ipv6 takes two tids */
- cudbg_is_ipv6_entry(tid_data, tcam_region) ? i += 2 : i++;
+ if (cudbg_is_ipv6_entry(tid_data, tcam_region)) {
+ /* T6 CLIP TCAM: ipv6 takes 4 entries */
+ if (is_t6(padap->params.chip) &&
+ i >= tcam_region.clip_start &&
+ i < tcam_region.clip_start + CUDBG_T6_CLIP)
+ i += 4;
+ else /* Main TCAM: ipv6 takes two tids */
+ i += 2;
+ } else {
+ i++;
+ }
tid_data++;
bytes += sizeof(struct cudbg_tid_data);
}
+out:
return cudbg_write_and_release_buff(pdbg_init, &temp_buff, dbg_buff);
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index 843f8cada1de..6b55aa2eb2a5 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -2999,6 +2999,7 @@
#define LE_DB_HASH_TID_BASE_A 0x19c30
#define LE_DB_HASH_TBL_BASE_ADDR_A 0x19c30
#define LE_DB_INT_CAUSE_A 0x19c3c
+#define LE_DB_CLCAM_TID_BASE_A 0x19df4
#define LE_DB_TID_HASHBASE_A 0x19df8
#define T6_LE_DB_HASH_TID_BASE_A 0x19df8
--
2.14.1
^ permalink raw reply related
* Re: [PATCH 12/14] net: sched: retry action check-insert on concurrent modification
From: Jiri Pirko @ 2018-05-16 14:13 UTC (permalink / raw)
To: Vlad Buslov
Cc: netdev, davem, jhs, xiyou.wangcong, pablo, kadlec, fw, ast,
daniel, edumazet, keescook, linux-kernel, netfilter-devel,
coreteam, kliteyn
In-Reply-To: <vbfvabn1zic.fsf@reg-r-vrt-018-180.mtr.labs.mlnx>
Wed, May 16, 2018 at 03:52:20PM CEST, vladbu@mellanox.com wrote:
>
>On Wed 16 May 2018 at 13:21, Jiri Pirko <jiri@resnulli.us> wrote:
>> Wed, May 16, 2018 at 02:43:58PM CEST, vladbu@mellanox.com wrote:
>>>
>>>On Wed 16 May 2018 at 12:26, Jiri Pirko <jiri@resnulli.us> wrote:
>>>> Wed, May 16, 2018 at 01:55:06PM CEST, vladbu@mellanox.com wrote:
>>>>>
>>>>>On Wed 16 May 2018 at 09:59, Jiri Pirko <jiri@resnulli.us> wrote:
>>>>>> Mon, May 14, 2018 at 04:27:13PM CEST, vladbu@mellanox.com wrote:
>>>>>>>Retry check-insert sequence in action init functions if action with same
>>>>>>>index was inserted concurrently.
>>>>>>>
>>>>>>>Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
>>>>>>>---
>>>>>>> net/sched/act_bpf.c | 8 +++++++-
>>>>>>> net/sched/act_connmark.c | 8 +++++++-
>>>>>>> net/sched/act_csum.c | 8 +++++++-
>>>>>>> net/sched/act_gact.c | 8 +++++++-
>>>>>>> net/sched/act_ife.c | 8 +++++++-
>>>>>>> net/sched/act_ipt.c | 8 +++++++-
>>>>>>> net/sched/act_mirred.c | 8 +++++++-
>>>>>>> net/sched/act_nat.c | 8 +++++++-
>>>>>>> net/sched/act_pedit.c | 8 +++++++-
>>>>>>> net/sched/act_police.c | 9 ++++++++-
>>>>>>> net/sched/act_sample.c | 8 +++++++-
>>>>>>> net/sched/act_simple.c | 9 ++++++++-
>>>>>>> net/sched/act_skbedit.c | 8 +++++++-
>>>>>>> net/sched/act_skbmod.c | 8 +++++++-
>>>>>>> net/sched/act_tunnel_key.c | 9 ++++++++-
>>>>>>> net/sched/act_vlan.c | 9 ++++++++-
>>>>>>> 16 files changed, 116 insertions(+), 16 deletions(-)
>>>>>>>
>>>>>>>diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
>>>>>>>index 5554bf7..7e20fdc 100644
>>>>>>>--- a/net/sched/act_bpf.c
>>>>>>>+++ b/net/sched/act_bpf.c
>>>>>>>@@ -299,10 +299,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
>>>>>>>
>>>>>>> parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
>>>>>>>
>>>>>>>+replay:
>>>>>>> if (!tcf_idr_check(tn, parm->index, act, bind)) {
>>>>>>> ret = tcf_idr_create(tn, parm->index, est, act,
>>>>>>> &act_bpf_ops, bind, true);
>>>>>>>- if (ret < 0)
>>>>>>>+ /* Action with specified index was created concurrently.
>>>>>>>+ * Check again.
>>>>>>>+ */
>>>>>>>+ if (parm->index && ret == -ENOSPC)
>>>>>>>+ goto replay;
>>>>>>>+ else if (ret)
>>>>>>
>>>>>> Hmm, looks like you are doing the same/very similar thing in every act
>>>>>> code. I think it would make sense to introduce a helper function for
>>>>>> this purpose.
>>>>>
>>>>>This code uses goto so it can't be easily refactored into standalone
>>>>>function. Could you specify which part of this code you suggest to
>>>>>extract?
>>>>
>>>> Hmm, looking at the code, I think that what would help is to have a
>>>> helper that would atomically check if index exists and if not, it would
>>>> allocate one. Something like:
>>>>
>>>>
>>>> int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
>>>> struct tc_action **a, int bind)
>>>> {
>>>> struct tcf_idrinfo *idrinfo = tn->idrinfo;
>>>> struct tc_action *p;
>>>> int err;
>>>>
>>>> spin_lock(&idrinfo->lock);
>>>> if (*index) {
>>>> p = idr_find(&idrinfo->action_idr, *index);
>>>> if (p) {
>>>> if (bind)
>>>> p->tcfa_bindcnt++;
>>>> p->tcfa_refcnt++;
>>>> *a = p;
>>>> err = 0;
>>>> } else {
>>>> *a = NULL;
>>>> err = idr_alloc_u32(idr, NULL, index,
>>>> *index, GFP_ATOMIC);
>>>> }
>>>> } else {
>>>> *index = 1;
>>>> *a = NULL;
>>>> err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
>>>> }
>>>> spin_unlock(&idrinfo->lock);
>>>> return err;
>>>> }
>>>>
>>>> The act code would just check if "a" is NULL and if so, it would call
>>>> tcf_idr_create() with allocated index as arg.
>>>
>>>What about multiple actions that have arbitrary code between initial
>>>check and idr allocation that is currently inside tcf_idr_create()?
>>
>> Why it would be a problem to have them after the allocation?
>
>Lets look at mirred for exmple:
> exists = tcf_idr_check(tn, parm->index, a, bind);
> if (exists && bind)
> return 0;
>
> switch (parm->eaction) {
> case TCA_EGRESS_MIRROR:
> case TCA_EGRESS_REDIR:
> case TCA_INGRESS_REDIR:
> case TCA_INGRESS_MIRROR:
> break;
> default:
> if (exists)
> tcf_idr_release(*a, bind);
> NL_SET_ERR_MSG_MOD(extack, "Unknown mirred option");
> return -EINVAL;
> }
> if (parm->ifindex) {
> dev = __dev_get_by_index(net, parm->ifindex);
> if (dev == NULL) {
> if (exists)
> tcf_idr_release(*a, bind);
> return -ENODEV;
> }
> mac_header_xmit = dev_is_mac_header_xmit(dev);
> } else {
> dev = NULL;
> }
>
> if (!exists) {
> if (!dev) {
> NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist");
> return -EINVAL;
> }
> ret = tcf_idr_create(tn, parm->index, est, a,
> &act_mirred_ops, bind, true);
> /* Action with specified index was created concurrently.
> * Check again.
> */
> if (parm->index && ret == -ENOSPC)
> goto replay;
> else if (ret)
> return ret;
>
>There are several returns and cleanup is only performed when action
>exists. So all code like that will have to be audited to also remove
>index from idr, otherwise idr handles leak on return.
Yeah. You have to take care of the error path.
>
>>
>> There is one issue though with my draft. tcf_idr_insert() function
>> which actually assigns a "p" pointer to the idr index is called later on.
>> Until that happens, the idr_find() would return NULL even if the index
>> is actually allocated. We cannot assign "p" in tcf_idr_check_alloc()
>> because it is allocated only later on in tcf_idr_create(). But that is
>> resolvable by the following trick:
>>
>> int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
>> struct tc_action **a, int bind)
>> {
>> struct tcf_idrinfo *idrinfo = tn->idrinfo;
>> struct tc_action *p;
>> int err;
>>
>> again:
>> spin_lock(&idrinfo->lock);
>> if (*index) {
>> p = idr_find(&idrinfo->action_idr, *index);
>> if (IS_ERR(p)) {
>> /* This means that another process allocated
>> * index but did not assign the pointer yet.
>> */
>> spin_unlock(&idrinfo->lock);
>> goto again;
>> }
>> if (p) {
>> if (bind)
>> p->tcfa_bindcnt++;
>> p->tcfa_refcnt++;
>> *a = p;
>> err = 0;
>> } else {
>> *a = NULL;
>> err = idr_alloc_u32(idr, NULL, index,
>> *index, GFP_ATOMIC);
>> idr_replace(&idrinfo->action_idr,
>> ERR_PTR(-EBUSY), *index);
>> }
>> } else {
>> *index = 1;
>> *a = NULL;
>> err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
>> idr_replace(&idrinfo->action_idr, ERR_PTR(-EBUSY), *index);
>> }
>> spin_unlock(&idrinfo->lock);
>> return err;
>> }
>>
>
>So users of action idr that might perform concurrent lookups are also
>have to be changed to check for error pointers, that now can be inserted
>into idr? Seems like a complex change...
You can just add a simple check into tcf_idr_lookup(). Where else?
>
>>
>>
>>
>>
>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>> [...]
>>>>>
>>>
>
^ permalink raw reply
* Re: [PATCH net-next] erspan: set bso bit based on mirrored packet's len
From: William Tu @ 2018-05-16 14:05 UTC (permalink / raw)
To: Tobin C. Harding; +Cc: Linux Kernel Network Developers
In-Reply-To: <20180515053342.GB10152@eros>
On Mon, May 14, 2018 at 10:33 PM, Tobin C. Harding <tobin@apporbit.com> wrote:
> On Mon, May 14, 2018 at 04:54:36PM -0700, William Tu wrote:
>> Before the patch, the erspan BSO bit (Bad/Short/Oversized) is not
>> handled. BSO has 4 possible values:
>> 00 --> Good frame with no error, or unknown integrity
>> 11 --> Payload is a Bad Frame with CRC or Alignment Error
>> 01 --> Payload is a Short Frame
>> 10 --> Payload is an Oversized Frame
>>
>> Based the short/oversized definitions in RFC1757, the patch sets
>> the bso bit based on the mirrored packet's size.
>>
>> Reported-by: Xiaoyan Jin <xiaoyanj@vmware.com>
>> Signed-off-by: William Tu <u9012063@gmail.com>
>> ---
>> include/net/erspan.h | 25 +++++++++++++++++++++++++
>> 1 file changed, 25 insertions(+)
>>
>> diff --git a/include/net/erspan.h b/include/net/erspan.h
>> index d044aa60cc76..5eb95f78ad45 100644
>> --- a/include/net/erspan.h
>> +++ b/include/net/erspan.h
>> @@ -219,6 +219,30 @@ static inline __be32 erspan_get_timestamp(void)
>> return htonl((u32)h_usecs);
>> }
>>
>> +/* ERSPAN BSO (Bad/Short/Oversized)
>> + * 00b --> Good frame with no error, or unknown integrity
>> + * 01b --> Payload is a Short Frame
>> + * 10b --> Payload is an Oversized Frame
>> + * 11b --> Payload is a Bad Frame with CRC or Alignment Error
>> + */
>> +enum erspan_bso {
>> + BSO_NOERROR,
>> + BSO_SHORT,
>> + BSO_OVERSIZED,
>> + BSO_BAD,
>> +};
>
> If we are relying on the values perhaps this would be clearer
>
> BSO_NOERROR = 0x00,
> BSO_SHORT = 0x01,
> BSO_OVERSIZED = 0x02,
> BSO_BAD = 0x03,
>
Yes, thanks. I will change in v2.
>> +
>> +static inline u8 erspan_detect_bso(struct sk_buff *skb)
>> +{
>> + if (skb->len < ETH_ZLEN)
>> + return BSO_SHORT;
>> +
>> + if (skb->len > ETH_FRAME_LEN)
>> + return BSO_OVERSIZED;
>> +
>> + return BSO_NOERROR;
>> +}
>
> Without having much contextual knowledge around this patch; should we be
> doing some check on CRC or alignment (at some stage)? Having BSO_BAD
> seems to imply so?
>
The definition of BSO_BAD:
etherStatsCRCAlignErrors OBJECT-TYPE
SYNTAX Counter
ACCESS read-only
STATUS mandatory
DESCRIPTION
"The total number of packets received that
had a length (excluding framing bits, but
including FCS octets) of between 64 and 1518
octets, inclusive, but but had either a bad
Frame Check Sequence (FCS) with an integral
number of octets (FCS Error) or a bad FCS with
a non-integral number of octets (Alignment Error)."
But I don't know how to check CRC error at this code point.
Isn't it done by the NIC hardware?
Thanks for your review!
William
^ permalink raw reply
* Re: [RFC v4 3/5] virtio_ring: add packed ring support
From: Jason Wang @ 2018-05-16 14:05 UTC (permalink / raw)
To: Tiwei Bie; +Cc: mst, virtualization, linux-kernel, netdev, wexu, jfreimann
In-Reply-To: <20180516134550.GB4171@debian>
On 2018年05月16日 21:45, Tiwei Bie wrote:
> On Wed, May 16, 2018 at 08:51:43PM +0800, Jason Wang wrote:
>> On 2018年05月16日 20:39, Tiwei Bie wrote:
>>> On Wed, May 16, 2018 at 07:50:16PM +0800, Jason Wang wrote:
>>>> On 2018年05月16日 16:37, Tiwei Bie wrote:
>>> [...]
>>>>> struct vring_virtqueue {
>>>>> @@ -116,6 +117,9 @@ struct vring_virtqueue {
>>>>> /* Last written value to driver->flags in
>>>>> * guest byte order. */
>>>>> u16 event_flags_shadow;
>>>>> +
>>>>> + /* ID allocation. */
>>>>> + struct idr buffer_id;
>>>> I'm not sure idr is fit for the performance critical case here. Need to
>>>> measure its performance impact, especially if we have few unused slots.
>>> I'm also not sure.. But fortunately, it should be quite easy
>>> to replace it with something else without changing other code.
>>> If it will really hurt the performance, I'll change it.
>> We may want to do some benchmarking/profiling to see.
> Yeah!
>
>>>>> };
>>>>> };
>>> [...]
>>>>> +static void detach_buf_packed(struct vring_virtqueue *vq, unsigned int head,
>>>>> + unsigned int id, void **ctx)
>>>>> +{
>>>>> + struct vring_packed_desc *desc;
>>>>> + unsigned int i, j;
>>>>> +
>>>>> + /* Clear data ptr. */
>>>>> + vq->desc_state[id].data = NULL;
>>>>> +
>>>>> + i = head;
>>>>> +
>>>>> + for (j = 0; j < vq->desc_state[id].num; j++) {
>>>>> + desc = &vq->vring_packed.desc[i];
>>>>> + vring_unmap_one_packed(vq, desc);
>>>> As mentioned in previous discussion, this probably won't work for the case
>>>> of out of order completion since it depends on the information in the
>>>> descriptor ring. We probably need to extend ctx to record such information.
>>> Above code doesn't depend on the information in the descriptor
>>> ring. The vq->desc_state[] is the extended ctx.
>>>
>>> Best regards,
>>> Tiwei Bie
>> Yes, but desc is a pointer to descriptor ring I think so
>> vring_unmap_one_packed() still depends on the content of descriptor ring?
>>
> I got your point now. I think it makes sense to reserve
> the bits of the addr field. Driver shouldn't try to get
> addrs from the descriptors when cleanup the descriptors
> no matter whether we support out-of-order or not.
Maybe I was wrong, but I remember spec mentioned something like this.
>
> But combining it with the out-of-order support, it will
> mean that the driver still needs to maintain a desc/ctx
> list that is very similar to the desc ring in the split
> ring. I'm not quite sure whether it's something we want.
> If it is true, I'll do it. So do you think we also want
> to maintain such a desc/ctx list for packed ring?
To make it work for OOO backends I think we need something like this
(hardware NIC drivers are usually have something like this).
Not for the patch, but it looks like having a OUT_OF_ORDER feature bit
is much more simpler to be started with.
Thanks
>
> Best regards,
> Tiwei Bie
^ permalink raw reply
* Re: [PATCH v2 1/2] media: rc: introduce BPF_PROG_RAWIR_EVENT
From: Sean Young @ 2018-05-16 13:55 UTC (permalink / raw)
To: linux-media, linux-kernel, Alexei Starovoitov,
Mauro Carvalho Chehab, Daniel Borkmann, netdev, Matthias Reichl,
Devin Heitmueller, Y Song
In-Reply-To: <82363bf25c059528b93fbe542d88f147b5081424.1526409690.git.sean@mess.org>
On Tue, May 15, 2018 at 07:50:19PM +0100, Sean Young wrote:
> Add support for BPF_PROG_RAWIR_EVENT. This type of BPF program can call
> rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> that the last key should be repeated.
>
> The bpf program can be attached to using the bpf(BPF_PROG_ATTACH) syscall;
> the target_fd must be the /dev/lircN device.
The locking of the list of attached bpf programs is broken in various ways.
I'll have to rework this for v3.
Sean
>
> Signed-off-by: Sean Young <sean@mess.org>
> ---
> drivers/media/rc/Kconfig | 10 +
> drivers/media/rc/Makefile | 1 +
> drivers/media/rc/bpf-rawir-event.c | 322 +++++++++++++++++++++++++++++
> drivers/media/rc/lirc_dev.c | 28 +++
> drivers/media/rc/rc-core-priv.h | 19 ++
> drivers/media/rc/rc-ir-raw.c | 3 +
> include/linux/bpf_rcdev.h | 30 +++
> include/linux/bpf_types.h | 3 +
> include/uapi/linux/bpf.h | 55 ++++-
> kernel/bpf/syscall.c | 7 +
> 10 files changed, 477 insertions(+), 1 deletion(-)
> create mode 100644 drivers/media/rc/bpf-rawir-event.c
> create mode 100644 include/linux/bpf_rcdev.h
>
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index eb2c3b6eca7f..55747af5b978 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -25,6 +25,16 @@ config LIRC
> passes raw IR to and from userspace, which is needed for
> IR transmitting (aka "blasting") and for the lirc daemon.
>
> +config BPF_RAWIR_EVENT
> + bool "Enable attaching BPF programs to lirc devices"
> + depends on BPF_SYSCALL
> + depends on RC_CORE=y
> + depends on LIRC
> + help
> + Enable this option to make it possible to load custom IR
> + decoders written in BPF. These decoders are type
> + BPF_PROG_TYPE_RAW_IR_EVENT.
> +
> menuconfig RC_DECODERS
> bool "Remote controller decoders"
> depends on RC_CORE
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 2e1c87066f6c..74907823bef8 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -5,6 +5,7 @@ obj-y += keymaps/
> obj-$(CONFIG_RC_CORE) += rc-core.o
> rc-core-y := rc-main.o rc-ir-raw.o
> rc-core-$(CONFIG_LIRC) += lirc_dev.o
> +rc-core-$(CONFIG_BPF_RAWIR_EVENT) += bpf-rawir-event.o
> obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
> obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
> obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
> diff --git a/drivers/media/rc/bpf-rawir-event.c b/drivers/media/rc/bpf-rawir-event.c
> new file mode 100644
> index 000000000000..8007841977d6
> --- /dev/null
> +++ b/drivers/media/rc/bpf-rawir-event.c
> @@ -0,0 +1,322 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// bpf-rawir-event.c - handles bpf
> +//
> +// Copyright (C) 2018 Sean Young <sean@mess.org>
> +
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include "rc-core-priv.h"
> +
> +/*
> + * BPF interface for raw IR
> + */
> +const struct bpf_prog_ops rawir_event_prog_ops = {
> +};
> +
> +BPF_CALL_1(bpf_rc_repeat, struct bpf_rawir_event*, event)
> +{
> + struct ir_raw_event_ctrl *ctrl;
> +
> + ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event);
> +
> + rc_repeat(ctrl->dev);
> +
> + return 0;
> +}
> +
> +static const struct bpf_func_proto rc_repeat_proto = {
> + .func = bpf_rc_repeat,
> + .gpl_only = true, /* rc_repeat is EXPORT_SYMBOL_GPL */
> + .ret_type = RET_INTEGER,
> + .arg1_type = ARG_PTR_TO_CTX,
> +};
> +
> +BPF_CALL_4(bpf_rc_keydown, struct bpf_rawir_event*, event, u32, protocol,
> + u32, scancode, u32, toggle)
> +{
> + struct ir_raw_event_ctrl *ctrl;
> +
> + ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event);
> +
> + rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
> +
> + return 0;
> +}
> +
> +static const struct bpf_func_proto rc_keydown_proto = {
> + .func = bpf_rc_keydown,
> + .gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */
> + .ret_type = RET_INTEGER,
> + .arg1_type = ARG_PTR_TO_CTX,
> + .arg2_type = ARG_ANYTHING,
> + .arg3_type = ARG_ANYTHING,
> + .arg4_type = ARG_ANYTHING,
> +};
> +
> +static const struct bpf_func_proto *
> +rawir_event_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
> +{
> + switch (func_id) {
> + case BPF_FUNC_rc_repeat:
> + return &rc_repeat_proto;
> + case BPF_FUNC_rc_keydown:
> + return &rc_keydown_proto;
> + case BPF_FUNC_map_lookup_elem:
> + return &bpf_map_lookup_elem_proto;
> + case BPF_FUNC_map_update_elem:
> + return &bpf_map_update_elem_proto;
> + case BPF_FUNC_map_delete_elem:
> + return &bpf_map_delete_elem_proto;
> + case BPF_FUNC_ktime_get_ns:
> + return &bpf_ktime_get_ns_proto;
> + case BPF_FUNC_tail_call:
> + return &bpf_tail_call_proto;
> + case BPF_FUNC_get_prandom_u32:
> + return &bpf_get_prandom_u32_proto;
> + default:
> + return NULL;
> + }
> +}
> +
> +static bool rawir_event_is_valid_access(int off, int size,
> + enum bpf_access_type type,
> + const struct bpf_prog *prog,
> + struct bpf_insn_access_aux *info)
> +{
> + /* struct bpf_rawir_event has two u32 fields */
> + if (type == BPF_WRITE)
> + return false;
> +
> + if (size != sizeof(__u32))
> + return false;
> +
> + if (!(off == offsetof(struct bpf_rawir_event, duration) ||
> + off == offsetof(struct bpf_rawir_event, type)))
> + return false;
> +
> + return true;
> +}
> +
> +const struct bpf_verifier_ops rawir_event_verifier_ops = {
> + .get_func_proto = rawir_event_func_proto,
> + .is_valid_access = rawir_event_is_valid_access
> +};
> +
> +#define BPF_MAX_PROGS 64
> +
> +static int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog)
> +{
> + struct ir_raw_event_ctrl *raw;
> + struct bpf_prog_array __rcu *old_array;
> + struct bpf_prog_array *new_array;
> + int ret, i, size;
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + return ret;
> +
> + raw = rcdev->raw;
> +
> + if (raw->progs) {
> + size = bpf_prog_array_length(raw->progs);
> + for (i = 0; i < size; i++) {
> + if (prog == raw->progs->progs[i]) {
> + ret = -EEXIST;
> + goto out;
> + }
> + }
> +
> + if (size >= BPF_MAX_PROGS) {
> + ret = -E2BIG;
> + goto out;
> + }
> + }
> +
> + old_array = raw->progs;
> + ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
> + if (ret < 0)
> + goto out;
> +
> + rcu_assign_pointer(raw->progs, new_array);
> + bpf_prog_array_free(old_array);
> +out:
> + mutex_unlock(&rcdev->lock);
> + return ret;
> +}
> +
> +static int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog)
> +{
> + struct ir_raw_event_ctrl *raw;
> + struct bpf_prog_array __rcu *old_array;
> + struct bpf_prog_array *new_array;
> + int ret;
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + return ret;
> +
> + raw = rcdev->raw;
> +
> + old_array = raw->progs;
> + ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
> + if (ret < 0) {
> + bpf_prog_array_delete_safe(old_array, prog);
> + } else {
> + rcu_assign_pointer(raw->progs, new_array);
> + bpf_prog_array_free(old_array);
> + }
> +
> + bpf_prog_put(prog);
> + mutex_unlock(&rcdev->lock);
> + return 0;
> +}
> +
> +void rc_dev_bpf_run(struct rc_dev *rcdev, struct ir_raw_event ev)
> +{
> + struct ir_raw_event_ctrl *raw = rcdev->raw;
> +
> + if (!raw->progs)
> + return;
> +
> + if (unlikely(ev.carrier_report)) {
> + raw->bpf_rawir_event.carrier = ev.carrier;
> + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_CARRIER;
> + } else {
> + raw->bpf_rawir_event.duration = ev.duration;
> +
> + if (ev.pulse)
> + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_PULSE;
> + else if (ev.timeout)
> + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_TIMEOUT;
> + else if (ev.reset)
> + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_RESET;
> + else
> + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_SPACE;
> + }
> +
> + BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_rawir_event, BPF_PROG_RUN);
> +}
> +
> +void rc_dev_bpf_put(struct rc_dev *rcdev)
> +{
> + struct bpf_prog_array *progs = rcdev->raw->progs;
> + int i, size;
> +
> + if (!progs)
> + return;
> +
> + size = bpf_prog_array_length(progs);
> + for (i = 0; i < size; i++)
> + bpf_prog_put(progs->progs[i]);
> +
> + bpf_prog_array_free(rcdev->raw->progs);
> +}
> +
> +int rc_dev_prog_attach(const union bpf_attr *attr)
> +{
> + struct bpf_prog *prog;
> + struct rc_dev *rcdev;
> + int ret;
> +
> + if (attr->attach_flags)
> + return -EINVAL;
> +
> + prog = bpf_prog_get_type(attr->attach_bpf_fd,
> + BPF_PROG_TYPE_RAWIR_EVENT);
> + if (IS_ERR(prog))
> + return PTR_ERR(prog);
> +
> + rcdev = rc_dev_get_from_fd(attr->target_fd);
> + if (IS_ERR(rcdev)) {
> + bpf_prog_put(prog);
> + return PTR_ERR(rcdev);
> + }
> +
> + ret = rc_dev_bpf_attach(rcdev, prog);
> + if (ret)
> + bpf_prog_put(prog);
> +
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> +
> +int rc_dev_prog_detach(const union bpf_attr *attr)
> +{
> + struct bpf_prog *prog;
> + struct rc_dev *rcdev;
> + int ret;
> +
> + if (attr->attach_flags)
> + return -EINVAL;
> +
> + prog = bpf_prog_get_type(attr->attach_bpf_fd,
> + BPF_PROG_TYPE_RAWIR_EVENT);
> + if (IS_ERR(prog))
> + return PTR_ERR(prog);
> +
> + rcdev = rc_dev_get_from_fd(attr->target_fd);
> + if (IS_ERR(rcdev)) {
> + bpf_prog_put(prog);
> + return PTR_ERR(rcdev);
> + }
> +
> + ret = rc_dev_bpf_detach(rcdev, prog);
> +
> + bpf_prog_put(prog);
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> +
> +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
> +{
> + __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
> + struct bpf_prog_array *progs;
> + struct rc_dev *rcdev;
> + u32 cnt, flags = 0;
> + int ret;
> +
> + if (attr->query.query_flags)
> + return -EINVAL;
> +
> + rcdev = rc_dev_get_from_fd(attr->query.target_fd);
> + if (IS_ERR(rcdev))
> + return PTR_ERR(rcdev);
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + goto out;
> +
> + progs = rcdev->raw->progs;
> + cnt = progs ? bpf_prog_array_length(progs) : 0;
> +
> + if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
> + ret = -EFAULT;
> + goto out;
> + }
> +
> + if (attr->query.prog_cnt != 0 && prog_ids && cnt)
> + ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
> +
> +out:
> + mutex_unlock(&rcdev->lock);
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
> index 24e9fbb80e81..c3028d0366d1 100644
> --- a/drivers/media/rc/lirc_dev.c
> +++ b/drivers/media/rc/lirc_dev.c
> @@ -20,6 +20,7 @@
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/device.h>
> +#include <linux/file.h>
> #include <linux/idr.h>
> #include <linux/poll.h>
> #include <linux/sched.h>
> @@ -816,4 +817,31 @@ void __exit lirc_dev_exit(void)
> unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
> }
>
> +struct rc_dev *rc_dev_get_from_fd(int fd)
> +{
> + struct rc_dev *dev;
> + struct file *f;
> +
> + f = fget_raw(fd);
> + if (!f)
> + return ERR_PTR(-EBADF);
> +
> + if (!S_ISCHR(f->f_inode->i_mode) ||
> + imajor(f->f_inode) != MAJOR(lirc_base_dev)) {
> + fput(f);
> + return ERR_PTR(-EBADF);
> + }
> +
> + dev = container_of(f->f_inode->i_cdev, struct rc_dev, lirc_cdev);
> + if (!dev->registered) {
> + fput(f);
> + return ERR_PTR(-ENODEV);
> + }
> +
> + get_device(&dev->dev);
> + fput(f);
> +
> + return dev;
> +}
> +
> MODULE_ALIAS("lirc_dev");
> diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
> index e0e6a17460f6..6db579f425f1 100644
> --- a/drivers/media/rc/rc-core-priv.h
> +++ b/drivers/media/rc/rc-core-priv.h
> @@ -13,6 +13,7 @@
> #define MAX_IR_EVENT_SIZE 512
>
> #include <linux/slab.h>
> +#include <uapi/linux/bpf.h>
> #include <media/rc-core.h>
>
> /**
> @@ -57,6 +58,11 @@ struct ir_raw_event_ctrl {
> /* raw decoder state follows */
> struct ir_raw_event prev_ev;
> struct ir_raw_event this_ev;
> +
> +#ifdef CONFIG_BPF_RAWIR_EVENT
> + struct bpf_rawir_event bpf_rawir_event;
> + struct bpf_prog_array *progs;
> +#endif
> struct nec_dec {
> int state;
> unsigned count;
> @@ -288,6 +294,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
> void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
> int ir_lirc_register(struct rc_dev *dev);
> void ir_lirc_unregister(struct rc_dev *dev);
> +struct rc_dev *rc_dev_get_from_fd(int fd);
> #else
> static inline int lirc_dev_init(void) { return 0; }
> static inline void lirc_dev_exit(void) {}
> @@ -299,4 +306,16 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
> static inline void ir_lirc_unregister(struct rc_dev *dev) { }
> #endif
>
> +/*
> + * bpf interface
> + */
> +#ifdef CONFIG_BPF_RAWIR_EVENT
> +void rc_dev_bpf_put(struct rc_dev *dev);
> +void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev);
> +#else
> +static inline void rc_dev_bpf_put(struct rc_dev *dev) { }
> +static inline void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev)
> +{ }
> +#endif
> +
> #endif /* _RC_CORE_PRIV */
> diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
> index 374f83105a23..25828f15faec 100644
> --- a/drivers/media/rc/rc-ir-raw.c
> +++ b/drivers/media/rc/rc-ir-raw.c
> @@ -32,6 +32,7 @@ static int ir_raw_event_thread(void *data)
> handler->protocols || !handler->protocols)
> handler->decode(raw->dev, ev);
> ir_lirc_raw_event(raw->dev, ev);
> + rc_dev_bpf_run(raw->dev, ev);
> raw->prev_ev = ev;
> }
> mutex_unlock(&ir_raw_handler_lock);
> @@ -623,6 +624,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
> handler->raw_unregister(dev);
> mutex_unlock(&ir_raw_handler_lock);
>
> + rc_dev_bpf_put(dev);
> +
> ir_raw_event_free(dev);
> }
>
> diff --git a/include/linux/bpf_rcdev.h b/include/linux/bpf_rcdev.h
> new file mode 100644
> index 000000000000..17a30f30436a
> --- /dev/null
> +++ b/include/linux/bpf_rcdev.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _BPF_RCDEV_H
> +#define _BPF_RCDEV_H
> +
> +#include <linux/bpf.h>
> +#include <uapi/linux/bpf.h>
> +
> +#ifdef CONFIG_BPF_RAWIR_EVENT
> +int rc_dev_prog_attach(const union bpf_attr *attr);
> +int rc_dev_prog_detach(const union bpf_attr *attr);
> +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr);
> +#else
> +static inline int rc_dev_prog_attach(const union bpf_attr *attr)
> +{
> + return -EINVAL;
> +}
> +
> +static inline int rc_dev_prog_detach(const union bpf_attr *attr)
> +{
> + return -EINVAL;
> +}
> +
> +static inline int rc_dev_prog_query(const union bpf_attr *attr,
> + union bpf_attr __user *uattr)
> +{
> + return -EINVAL;
> +}
> +#endif
> +
> +#endif /* _BPF_RCDEV_H */
> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> index d7df1b323082..667d1d557090 100644
> --- a/include/linux/bpf_types.h
> +++ b/include/linux/bpf_types.h
> @@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
> #ifdef CONFIG_CGROUP_BPF
> BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
> #endif
> +#ifdef CONFIG_BPF_RAWIR_EVENT
> +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_EVENT, rawir_event)
> +#endif
>
> BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
> BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 02e4112510f8..8ba1be825af0 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -140,6 +140,7 @@ enum bpf_prog_type {
> BPF_PROG_TYPE_SK_MSG,
> BPF_PROG_TYPE_RAW_TRACEPOINT,
> BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> + BPF_PROG_TYPE_RAWIR_EVENT,
> };
>
> enum bpf_attach_type {
> @@ -157,6 +158,7 @@ enum bpf_attach_type {
> BPF_CGROUP_INET6_CONNECT,
> BPF_CGROUP_INET4_POST_BIND,
> BPF_CGROUP_INET6_POST_BIND,
> + BPF_RAWIR_EVENT,
> __MAX_BPF_ATTACH_TYPE
> };
>
> @@ -1855,6 +1857,35 @@ union bpf_attr {
> * Egress device index on success, 0 if packet needs to continue
> * up the stack for further processing or a negative error in case
> * of failure.
> + *
> + * int bpf_rc_keydown(void *ctx, u32 protocol, u32 scancode, u32 toggle)
> + * Description
> + * Report decoded scancode with toggle value. For use in
> + * BPF_PROG_TYPE_RAWIR_EVENT, to report a successfully
> + * decoded scancode. This is will generate a keydown event,
> + * and a keyup event once the scancode is no longer repeated.
> + *
> + * *ctx* pointer to bpf_rawir_event, *protocol* is decoded
> + * protocol (see RC_PROTO_* enum).
> + *
> + * Some protocols include a toggle bit, in case the button
> + * was released and pressed again between consecutive scancodes,
> + * copy this bit into *toggle* if it exists, else set to 0.
> + *
> + * Return
> + * Always return 0 (for now)
> + *
> + * int bpf_rc_repeat(void *ctx)
> + * Description
> + * Repeat the last decoded scancode; some IR protocols like
> + * NEC have a special IR message for repeat last button,
> + * in case user is holding a button down; the scancode is
> + * not repeated.
> + *
> + * *ctx* pointer to bpf_rawir_event.
> + *
> + * Return
> + * Always return 0 (for now)
> */
> #define __BPF_FUNC_MAPPER(FN) \
> FN(unspec), \
> @@ -1926,7 +1957,9 @@ union bpf_attr {
> FN(skb_get_xfrm_state), \
> FN(get_stack), \
> FN(skb_load_bytes_relative), \
> - FN(fib_lookup),
> + FN(fib_lookup), \
> + FN(rc_repeat), \
> + FN(rc_keydown),
>
> /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> * function eBPF program intends to call
> @@ -1993,6 +2026,26 @@ enum bpf_hdr_start_off {
> BPF_HDR_START_NET,
> };
>
> +/*
> + * user accessible mirror of in-kernel ir_raw_event
> + */
> +#define BPF_RAWIR_EVENT_SPACE 0
> +#define BPF_RAWIR_EVENT_PULSE 1
> +#define BPF_RAWIR_EVENT_TIMEOUT 2
> +#define BPF_RAWIR_EVENT_RESET 3
> +#define BPF_RAWIR_EVENT_CARRIER 4
> +#define BPF_RAWIR_EVENT_DUTY_CYCLE 5
> +
> +struct bpf_rawir_event {
> + union {
> + __u32 duration;
> + __u32 carrier;
> + __u32 duty_cycle;
> + };
> +
> + __u32 type;
> +};
> +
> /* user accessible mirror of in-kernel sk_buff.
> * new fields can only be added to the end of this structure
> */
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index e2aeb5e89f44..75c089f407c8 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -11,6 +11,7 @@
> */
> #include <linux/bpf.h>
> #include <linux/bpf_trace.h>
> +#include <linux/bpf_rcdev.h>
> #include <linux/btf.h>
> #include <linux/syscalls.h>
> #include <linux/slab.h>
> @@ -1567,6 +1568,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)
> case BPF_SK_SKB_STREAM_PARSER:
> case BPF_SK_SKB_STREAM_VERDICT:
> return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true);
> + case BPF_RAWIR_EVENT:
> + return rc_dev_prog_attach(attr);
> default:
> return -EINVAL;
> }
> @@ -1637,6 +1640,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)
> case BPF_SK_SKB_STREAM_PARSER:
> case BPF_SK_SKB_STREAM_VERDICT:
> return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false);
> + case BPF_RAWIR_EVENT:
> + return rc_dev_prog_detach(attr);
> default:
> return -EINVAL;
> }
> @@ -1684,6 +1689,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
> case BPF_CGROUP_SOCK_OPS:
> case BPF_CGROUP_DEVICE:
> break;
> + case BPF_RAWIR_EVENT:
> + return rc_dev_prog_query(attr, uattr);
> default:
> return -EINVAL;
> }
> --
> 2.17.0
^ permalink raw reply
* Re: [PATCH 12/14] net: sched: retry action check-insert on concurrent modification
From: Vlad Buslov @ 2018-05-16 13:52 UTC (permalink / raw)
To: Jiri Pirko
Cc: netdev, davem, jhs, xiyou.wangcong, pablo, kadlec, fw, ast,
daniel, edumazet, keescook, linux-kernel, netfilter-devel,
coreteam, kliteyn
In-Reply-To: <20180516132135.GN1972@nanopsycho>
On Wed 16 May 2018 at 13:21, Jiri Pirko <jiri@resnulli.us> wrote:
> Wed, May 16, 2018 at 02:43:58PM CEST, vladbu@mellanox.com wrote:
>>
>>On Wed 16 May 2018 at 12:26, Jiri Pirko <jiri@resnulli.us> wrote:
>>> Wed, May 16, 2018 at 01:55:06PM CEST, vladbu@mellanox.com wrote:
>>>>
>>>>On Wed 16 May 2018 at 09:59, Jiri Pirko <jiri@resnulli.us> wrote:
>>>>> Mon, May 14, 2018 at 04:27:13PM CEST, vladbu@mellanox.com wrote:
>>>>>>Retry check-insert sequence in action init functions if action with same
>>>>>>index was inserted concurrently.
>>>>>>
>>>>>>Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
>>>>>>---
>>>>>> net/sched/act_bpf.c | 8 +++++++-
>>>>>> net/sched/act_connmark.c | 8 +++++++-
>>>>>> net/sched/act_csum.c | 8 +++++++-
>>>>>> net/sched/act_gact.c | 8 +++++++-
>>>>>> net/sched/act_ife.c | 8 +++++++-
>>>>>> net/sched/act_ipt.c | 8 +++++++-
>>>>>> net/sched/act_mirred.c | 8 +++++++-
>>>>>> net/sched/act_nat.c | 8 +++++++-
>>>>>> net/sched/act_pedit.c | 8 +++++++-
>>>>>> net/sched/act_police.c | 9 ++++++++-
>>>>>> net/sched/act_sample.c | 8 +++++++-
>>>>>> net/sched/act_simple.c | 9 ++++++++-
>>>>>> net/sched/act_skbedit.c | 8 +++++++-
>>>>>> net/sched/act_skbmod.c | 8 +++++++-
>>>>>> net/sched/act_tunnel_key.c | 9 ++++++++-
>>>>>> net/sched/act_vlan.c | 9 ++++++++-
>>>>>> 16 files changed, 116 insertions(+), 16 deletions(-)
>>>>>>
>>>>>>diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
>>>>>>index 5554bf7..7e20fdc 100644
>>>>>>--- a/net/sched/act_bpf.c
>>>>>>+++ b/net/sched/act_bpf.c
>>>>>>@@ -299,10 +299,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
>>>>>>
>>>>>> parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
>>>>>>
>>>>>>+replay:
>>>>>> if (!tcf_idr_check(tn, parm->index, act, bind)) {
>>>>>> ret = tcf_idr_create(tn, parm->index, est, act,
>>>>>> &act_bpf_ops, bind, true);
>>>>>>- if (ret < 0)
>>>>>>+ /* Action with specified index was created concurrently.
>>>>>>+ * Check again.
>>>>>>+ */
>>>>>>+ if (parm->index && ret == -ENOSPC)
>>>>>>+ goto replay;
>>>>>>+ else if (ret)
>>>>>
>>>>> Hmm, looks like you are doing the same/very similar thing in every act
>>>>> code. I think it would make sense to introduce a helper function for
>>>>> this purpose.
>>>>
>>>>This code uses goto so it can't be easily refactored into standalone
>>>>function. Could you specify which part of this code you suggest to
>>>>extract?
>>>
>>> Hmm, looking at the code, I think that what would help is to have a
>>> helper that would atomically check if index exists and if not, it would
>>> allocate one. Something like:
>>>
>>>
>>> int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
>>> struct tc_action **a, int bind)
>>> {
>>> struct tcf_idrinfo *idrinfo = tn->idrinfo;
>>> struct tc_action *p;
>>> int err;
>>>
>>> spin_lock(&idrinfo->lock);
>>> if (*index) {
>>> p = idr_find(&idrinfo->action_idr, *index);
>>> if (p) {
>>> if (bind)
>>> p->tcfa_bindcnt++;
>>> p->tcfa_refcnt++;
>>> *a = p;
>>> err = 0;
>>> } else {
>>> *a = NULL;
>>> err = idr_alloc_u32(idr, NULL, index,
>>> *index, GFP_ATOMIC);
>>> }
>>> } else {
>>> *index = 1;
>>> *a = NULL;
>>> err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
>>> }
>>> spin_unlock(&idrinfo->lock);
>>> return err;
>>> }
>>>
>>> The act code would just check if "a" is NULL and if so, it would call
>>> tcf_idr_create() with allocated index as arg.
>>
>>What about multiple actions that have arbitrary code between initial
>>check and idr allocation that is currently inside tcf_idr_create()?
>
> Why it would be a problem to have them after the allocation?
Lets look at mirred for exmple:
exists = tcf_idr_check(tn, parm->index, a, bind);
if (exists && bind)
return 0;
switch (parm->eaction) {
case TCA_EGRESS_MIRROR:
case TCA_EGRESS_REDIR:
case TCA_INGRESS_REDIR:
case TCA_INGRESS_MIRROR:
break;
default:
if (exists)
tcf_idr_release(*a, bind);
NL_SET_ERR_MSG_MOD(extack, "Unknown mirred option");
return -EINVAL;
}
if (parm->ifindex) {
dev = __dev_get_by_index(net, parm->ifindex);
if (dev == NULL) {
if (exists)
tcf_idr_release(*a, bind);
return -ENODEV;
}
mac_header_xmit = dev_is_mac_header_xmit(dev);
} else {
dev = NULL;
}
if (!exists) {
if (!dev) {
NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist");
return -EINVAL;
}
ret = tcf_idr_create(tn, parm->index, est, a,
&act_mirred_ops, bind, true);
/* Action with specified index was created concurrently.
* Check again.
*/
if (parm->index && ret == -ENOSPC)
goto replay;
else if (ret)
return ret;
There are several returns and cleanup is only performed when action
exists. So all code like that will have to be audited to also remove
index from idr, otherwise idr handles leak on return.
>
> There is one issue though with my draft. tcf_idr_insert() function
> which actually assigns a "p" pointer to the idr index is called later on.
> Until that happens, the idr_find() would return NULL even if the index
> is actually allocated. We cannot assign "p" in tcf_idr_check_alloc()
> because it is allocated only later on in tcf_idr_create(). But that is
> resolvable by the following trick:
>
> int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
> struct tc_action **a, int bind)
> {
> struct tcf_idrinfo *idrinfo = tn->idrinfo;
> struct tc_action *p;
> int err;
>
> again:
> spin_lock(&idrinfo->lock);
> if (*index) {
> p = idr_find(&idrinfo->action_idr, *index);
> if (IS_ERR(p)) {
> /* This means that another process allocated
> * index but did not assign the pointer yet.
> */
> spin_unlock(&idrinfo->lock);
> goto again;
> }
> if (p) {
> if (bind)
> p->tcfa_bindcnt++;
> p->tcfa_refcnt++;
> *a = p;
> err = 0;
> } else {
> *a = NULL;
> err = idr_alloc_u32(idr, NULL, index,
> *index, GFP_ATOMIC);
> idr_replace(&idrinfo->action_idr,
> ERR_PTR(-EBUSY), *index);
> }
> } else {
> *index = 1;
> *a = NULL;
> err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
> idr_replace(&idrinfo->action_idr, ERR_PTR(-EBUSY), *index);
> }
> spin_unlock(&idrinfo->lock);
> return err;
> }
>
So users of action idr that might perform concurrent lookups are also
have to be changed to check for error pointers, that now can be inserted
into idr? Seems like a complex change...
>
>
>
>
>>
>>>
>>>
>>>>
>>>>>
>>>>> [...]
>>>>
>>
^ permalink raw reply
* Re: [RFC v4 3/5] virtio_ring: add packed ring support
From: Tiwei Bie @ 2018-05-16 13:45 UTC (permalink / raw)
To: Jason Wang; +Cc: mst, virtualization, linux-kernel, netdev, wexu, jfreimann
In-Reply-To: <ecb50c84-b412-ff0b-6c52-fd789b6c8a86@redhat.com>
On Wed, May 16, 2018 at 08:51:43PM +0800, Jason Wang wrote:
> On 2018年05月16日 20:39, Tiwei Bie wrote:
> > On Wed, May 16, 2018 at 07:50:16PM +0800, Jason Wang wrote:
> > > On 2018年05月16日 16:37, Tiwei Bie wrote:
> > [...]
> > > > struct vring_virtqueue {
> > > > @@ -116,6 +117,9 @@ struct vring_virtqueue {
> > > > /* Last written value to driver->flags in
> > > > * guest byte order. */
> > > > u16 event_flags_shadow;
> > > > +
> > > > + /* ID allocation. */
> > > > + struct idr buffer_id;
> > > I'm not sure idr is fit for the performance critical case here. Need to
> > > measure its performance impact, especially if we have few unused slots.
> > I'm also not sure.. But fortunately, it should be quite easy
> > to replace it with something else without changing other code.
> > If it will really hurt the performance, I'll change it.
>
> We may want to do some benchmarking/profiling to see.
Yeah!
>
> >
> > > > };
> > > > };
> > [...]
> > > > +static void detach_buf_packed(struct vring_virtqueue *vq, unsigned int head,
> > > > + unsigned int id, void **ctx)
> > > > +{
> > > > + struct vring_packed_desc *desc;
> > > > + unsigned int i, j;
> > > > +
> > > > + /* Clear data ptr. */
> > > > + vq->desc_state[id].data = NULL;
> > > > +
> > > > + i = head;
> > > > +
> > > > + for (j = 0; j < vq->desc_state[id].num; j++) {
> > > > + desc = &vq->vring_packed.desc[i];
> > > > + vring_unmap_one_packed(vq, desc);
> > > As mentioned in previous discussion, this probably won't work for the case
> > > of out of order completion since it depends on the information in the
> > > descriptor ring. We probably need to extend ctx to record such information.
> > Above code doesn't depend on the information in the descriptor
> > ring. The vq->desc_state[] is the extended ctx.
> >
> > Best regards,
> > Tiwei Bie
>
> Yes, but desc is a pointer to descriptor ring I think so
> vring_unmap_one_packed() still depends on the content of descriptor ring?
>
I got your point now. I think it makes sense to reserve
the bits of the addr field. Driver shouldn't try to get
addrs from the descriptors when cleanup the descriptors
no matter whether we support out-of-order or not.
But combining it with the out-of-order support, it will
mean that the driver still needs to maintain a desc/ctx
list that is very similar to the desc ring in the split
ring. I'm not quite sure whether it's something we want.
If it is true, I'll do it. So do you think we also want
to maintain such a desc/ctx list for packed ring?
Best regards,
Tiwei Bie
^ permalink raw reply
* Re: [iproute2] Bug#898840: Latest update breaks ip6 default gateway cli api
From: Luca Boccassi @ 2018-05-16 13:42 UTC (permalink / raw)
To: Serhey Popovych, Stephen Hemminger; +Cc: Hans van Kranenburg, 898840, netdev
In-Reply-To: <4de26023-48bf-fb98-2592-6fe5a6bc5c49@mendix.com>
[-- Attachment #1: Type: text/plain, Size: 1439 bytes --]
On Wed, 2018-05-16 at 14:26 +0200, Hans van Kranenburg wrote:
> Package: iproute2
> Version: 4.16.0-2
> Severity: normal
>
> Hi,
>
> The last iproute2 update has a backwards incompatible change in
> setting
> IPv6 default routes, breaking existing configuration and scripts.
>
> Previously, the following was possible, and now it requires an
> explicit
> -6 option to be added:
>
> -# ip route add default via 2001:db8::1 dev eth0
> Error: inet address is expected rather than "2001:db8::1".
>
> This works:
> -# ip -6 route add default via 2001:db8::1 dev eth0
>
> I found out after having systems end up being unreachable after a
> reboot, because I have commands like these in network/interfaces.
>
> I had a look at upstream changelogs, but I don't see any mention of
> this, and suspect it was not intentional. However, it's bad.
Hello Serhey and Stephen,
Hans reported a regression in v4.16.0, ip route now requires -6 to be
manually added when using v6 addresses while up to 4.15 it didn't, the
commands quoted show the problem.
Bisecting shows that the following commit from Serhey introduced the
problem:
93fa12418dc6f5943692250244be303bb162175b
utils: Always specify family and ->bytelen in get_prefix_1()
Could you please have a look when you have a moment? It's very easy to
reproduce, and it breaks existing scripts and so on.
Thanks!
--
Kind regards,
Luca Boccassi
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: Hangs in r8152 connected to power management in kernels at least up v4.17-rc4
From: Jiri Slaby @ 2018-05-16 13:36 UTC (permalink / raw)
To: Hayes Wang, Oliver Neukum; +Cc: netdev@vger.kernel.org
In-Reply-To: <60052c70-ae38-7be8-5f70-5598104a13bd@suse.cz>
On 05/16/2018, 03:29 PM, Jiri Slaby wrote:
> On 05/16/2018, 02:07 PM, Hayes Wang wrote:
>> Oliver Neukum [mailto:oneukum@suse.com]
>>> Sent: Wednesday, May 16, 2018 6:10 PM
>> [...]
>>>> Besides, I find a similar issue as following.
>>>> https://www.spinics.net/lists/netdev/msg493512.html
>>>
>>> Well, if we have an imbalance in NAPI it should strike whereever
>>> it is used, not just in suspend(). Is there debugging for NAPI
>>> we could activate?
>>
>> No. The driver doesn't embed such debugging about it.
>
> Despite of that, Oliver, I have a kernel with a debug patch (attached)
> at (suse-only link):
> https://build.suse.de/project/show/home:jirislaby:stable-drm
>
>> And I don't find an imbalance between napi_disable() and napi_enable().
>
> There is none, apparently (the warns never triggered). BUt still the
> driver sucks wrt both power mgmt and dock plug/unplug. Since I am using
> the patch (it upper-bounds the napi_disable loop count) and the udev
> rule below, I can really use the nic.
BTW the added warning to napi_disable indeed triggers:
> xzgrep -a -B 2 kernel:.*WARNING.*napi messages-20180*
> messages-20180503.xz:2018-04-27T09:57:00.048922+02:00 anemoi2 kernel: [158616.363052] ------------[ cut here ]------------
> messages-20180503.xz:2018-04-27T09:57:00.048979+02:00 anemoi2 kernel: [158616.363070] NAPI_STATE_SCHED never cleared
> messages-20180503.xz:2018-04-27T09:57:00.048988+02:00 anemoi2 kernel: [158616.363120] WARNING: CPU: 1 PID: 14365 at ../net/core/dev.c:5665 napi_disable+0x3d/0x80
And since I do 'ip l set dev ethX down' before unplugging the dock with
the NIC, I have not seen a single occurrence.
So I assume it must be a problem of making usb->disconnect without prior
ndo->close (or alike).
thanks,
--
js
suse labs
^ permalink raw reply
* Re: i.MX6S/DL and QCA8334 switch using DSA driver - CPU port not working
From: Michal Vokáč @ 2018-05-16 13:32 UTC (permalink / raw)
To: Andrew Lunn; +Cc: Florian Fainelli, netdev, Vivien Didelot
In-Reply-To: <20180516131753.GG22000@lunn.ch>
On 16.5.2018 15:17, Andrew Lunn wrote:
>> So as the fixed-link subnode is optional we still should force some sensible
>> defaults if it is not used. Same as Marvell drives does. Can I say that we
>> agreed that the current CPU port setting is not correct and the fastest link
>> speed and duplex supported by the chip should be set as a default?
>
> That is a sensible default.
OK.
>>> As far as people using this driver, John submitted it, but we have not
>>> see many fixes/enhancements, so I am not clear who is actually using it
>>> these days... glad you are though!
>>
>> Thanks.
>>
>> To the compatibility of the driver with the QCA8334 that I use. I am not sure
>> what should be the correct way to deal with that. For example Marvell binding
>> uses only two compatible strings for a large range of chips in one family.
>> While Broadcom has one compatible string for each chip.
>
> With the Marvell devices, there is an ID register which tells you the
> model. The compatible string tells us the information we need in order
> to find that ID register. Once we know the ID, we have all the
> information we need, so don't need a more specific compatible string.
>
>> As I mentioned earlier
>> in this thread the QCA8334 switch has four ports while the already supported
>> QCA8337 has seven ports. That is the only difference. Register address space
>> is the same.
>
> Can you identify the exact model using some ID register? If yes, than
> the existing compatible string is sufficient. If no, you need an
> additional compatible string.
Nope. Device ID is the same for the whole family so we need a new compatible.
--
Michal
^ permalink raw reply
* Re: [PATCH net-next v2 15/15] arm64: dts: allwinner: a64: add SRAM controller device tree node
From: Maxime Ripard @ 2018-05-16 13:31 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Icenowy Zheng, linux-arm-kernel, Mark Rutland, devicetree,
Stephen Boyd, netdev, Michael Turquette, Rob Herring,
Corentin Labbe, Mark Brown, Giuseppe Cavallaro, linux-clk
In-Reply-To: <CAGb2v65uZeueE=2FfsxWen9zNvCtMsJ+b=KgMfSh-ZKmO+S=cQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 4635 bytes --]
Hi,
On Tue, May 15, 2018 at 11:47:16PM -0700, Chen-Yu Tsai wrote:
> On Mon, May 14, 2018 at 1:03 AM, Maxime Ripard
> <maxime.ripard@bootlin.com> wrote:
> > 1;5201;0c
> > On Sun, May 13, 2018 at 12:37:49PM -0700, Chen-Yu Tsai wrote:
> >> On Wed, May 2, 2018 at 4:54 AM, Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> >> > On Wed, May 02, 2018 at 06:19:51PM +0800, Icenowy Zheng wrote:
> >> >>
> >> >>
> >> >> 于 2018年5月2日 GMT+08:00 下午5:53:21, Chen-Yu Tsai <wens@csie.org> 写到:
> >> >> >On Wed, May 2, 2018 at 5:51 PM, Maxime Ripard
> >> >> ><maxime.ripard@bootlin.com> wrote:
> >> >> >> Hi,
> >> >> >>
> >> >> >> On Wed, May 02, 2018 at 12:12:27AM +0800, Chen-Yu Tsai wrote:
> >> >> >>> From: Icenowy Zheng <icenowy@aosc.io>
> >> >> >>>
> >> >> >>> Allwinner A64 has a SRAM controller, and in the device tree
> >> >> >currently
> >> >> >>> we have a syscon node to enable EMAC driver to access the EMAC clock
> >> >> >>> register. As SRAM controller driver can now export regmap for this
> >> >> >>> register, replace the syscon node to the SRAM controller device
> >> >> >node,
> >> >> >>> and let EMAC driver to acquire its EMAC clock regmap.
> >> >> >>>
> >> >> >>> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
> >> >> >>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> >> >> >>> ---
> >> >> >>> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 23
> >> >> >+++++++++++++++----
> >> >> >>> 1 file changed, 19 insertions(+), 4 deletions(-)
> >> >> >>>
> >> >> >>> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> >> >> >b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> >> >> >>> index 1b2ef28c42bd..1c37659d9d41 100644
> >> >> >>> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> >> >> >>> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> >> >> >>> @@ -168,10 +168,25 @@
> >> >> >>> #size-cells = <1>;
> >> >> >>> ranges;
> >> >> >>>
> >> >> >>> - syscon: syscon@1c00000 {
> >> >> >>> - compatible =
> >> >> >"allwinner,sun50i-a64-system-controller",
> >> >> >>> - "syscon";
> >> >> >>> + sram_controller: sram-controller@1c00000 {
> >> >> >>> + compatible =
> >> >> >"allwinner,sun50i-a64-sram-controller";
> >> >> >>
> >> >> >> I don't think there's anything preventing us from keeping the
> >> >> >> -system-controller compatible. It's what was in the DT before, and
> >> >> >> it's how it's called in the datasheet.
> >> >> >
> >> >> >I actually meant to ask you about this. The -system-controller
> >> >> >compatible matches the datasheet better. Maybe we should just
> >> >> >switch to that one?
> >> >>
> >> >> No, if we do the switch the system-controller compatible,
> >> >> the device will be probed on the same memory region with
> >> >> a syscon on old DTs.
> >> >
> >> > The device hasn't magically changed either. Maybe we just need to add
> >> > a check to make sure we don't have the syscon compatible in the SRAM
> >> > driver probe so that the double driver issue doesn't happen?
> >>
> >> The syscon interface (which is not even a full blown device driver)
> >> only looks at the "syscon" compatible. Either way we're removing that
> >> part from the device tree so things should be ok for new device trees.
> >> As Maxime mentioned we can do a check for the syscon compatible and
> >> either give a warning to the user asking them to update their device
> >> tree, or not register our custom regmap, or not probe the SRAM driver.
> >> Personally I prefer the first option. The system controller block is
> >> probed before any syscon users, so we should be fine, given the dwmac
> >> driver goes the custom regmap path first.
> >>
> >> BTW, I still might end up changing the compatible. The manual uses
> >> "system control", not "system controller", which I think makes sense,
> >> since it is just a bunch of register files, kind of like the GRF
> >> (General Register Files) block found in Rockchip SoCs [1], and not an
> >> actual "controller".
> >
> > I'm not really fond of that, but we should at least make it consistent
> > on the other patches Paul sent then.
>
> For the A10s / A13 right?
And A33, yep.
> I think my naming is slightly better, but it's just a minor detail.
Let's do this then.
> While we're still debating this, can I merge the R40 stuff first?
> The driver bits are already in.
Yep, go ahead.
Maxime
--
Maxime Ripard, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [RFC v4 4/5] virtio_ring: add event idx support in packed ring
From: Jason Wang @ 2018-05-16 13:31 UTC (permalink / raw)
To: Tiwei Bie; +Cc: mst, virtualization, linux-kernel, netdev, wexu, jfreimann
In-Reply-To: <20180516125825.GA4171@debian>
On 2018年05月16日 20:58, Tiwei Bie wrote:
> On Wed, May 16, 2018 at 08:17:21PM +0800, Jason Wang wrote:
>> On 2018年05月16日 16:37, Tiwei Bie wrote:
> [...]
>>> @@ -1160,15 +1186,27 @@ static void virtqueue_disable_cb_packed(struct virtqueue *_vq)
>>> static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)
>>> {
>>> struct vring_virtqueue *vq = to_vvq(_vq);
>>> + u16 wrap_counter;
>>> START_USE(vq);
>>> /* We optimistically turn back on interrupts, then check if there was
>>> * more to do. */
>>> + /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
>>> + * either clear the flags bit or point the event index at the next
>>> + * entry. Always update the event index to keep code simple. */
>>> +
>>> + wrap_counter = vq->wrap_counter;
>>> + if (vq->last_used_idx > vq->next_avail_idx)
>> Should this be ">=" consider rx refill may try to completely fill the ring?
> It seems that there are two cases that last_used_idx
> equals to next_avail_idx. The first one is that the
> ring is empty. And the second one is that the ring
> is full. Although in the first case, most probably,
> the driver won't enable the interrupt.
>
> Maybe I really should track the used_wrap_counter
> instead of calculating it each time I need it.. I'll
> give it a try..
>
Right, good to know and this will match spec sample code.
Thanks
^ permalink raw reply
* Re: Hangs in r8152 connected to power management in kernels at least up v4.17-rc4
From: Jiri Slaby @ 2018-05-16 13:29 UTC (permalink / raw)
To: Hayes Wang, Oliver Neukum; +Cc: netdev@vger.kernel.org
In-Reply-To: <0835B3720019904CB8F7AA43166CEEB2D2E47F8B@RTITMBSV06.realtek.com.tw>
[-- Attachment #1: Type: text/plain, Size: 1143 bytes --]
On 05/16/2018, 02:07 PM, Hayes Wang wrote:
> Oliver Neukum [mailto:oneukum@suse.com]
>> Sent: Wednesday, May 16, 2018 6:10 PM
> [...]
>>> Besides, I find a similar issue as following.
>>> https://www.spinics.net/lists/netdev/msg493512.html
>>
>> Well, if we have an imbalance in NAPI it should strike whereever
>> it is used, not just in suspend(). Is there debugging for NAPI
>> we could activate?
>
> No. The driver doesn't embed such debugging about it.
Despite of that, Oliver, I have a kernel with a debug patch (attached)
at (suse-only link):
https://build.suse.de/project/show/home:jirislaby:stable-drm
> And I don't find an imbalance between napi_disable() and napi_enable().
There is none, apparently (the warns never triggered). BUt still the
driver sucks wrt both power mgmt and dock plug/unplug. Since I am using
the patch (it upper-bounds the napi_disable loop count) and the udev
rule below, I can really use the nic.
$ cat /etc/udev/rules.d/10-disable-r8152-pm.rules
ACTION=="add", SUBSYSTEM=="usb", ATTR{idProduct}=="8153",
ATTR{idVendor}=="0bda", TEST=="power/control", ATTR{power/control}="on"
thanks,
--
js
suse labs
[-- Attachment #2: r8152.patch --]
[-- Type: text/x-patch, Size: 5467 bytes --]
---
drivers/net/usb/r8152.c | 62 +++++++++++++++++++++++++++++++-----------------
net/core/dev.c | 14 +++++++++-
2 files changed, 53 insertions(+), 23 deletions(-)
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -704,6 +704,8 @@ struct r8152 {
unsigned long flags;
struct usb_device *udev;
struct napi_struct napi;
+ int napi_stat;
+ void *napi_last_en, *napi_last_dis;
struct usb_interface *intf;
struct net_device *netdev;
struct urb *intr_urb;
@@ -775,6 +777,31 @@ static unsigned int agg_buf_sz = 16384;
#define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \
VLAN_ETH_HLEN - ETH_FCS_LEN)
+static void my_napi_enable(struct r8152 *tp)
+{
+ if (tp->napi_stat == 0) {
+ napi_enable(&tp->napi);
+ tp->napi_stat++;
+ tp->napi_last_en = (void *)_RET_IP_;
+ return;
+ }
+
+ WARN(1, "napi_stat=%d\n", tp->napi_stat);
+}
+
+static void my_napi_disable(struct r8152 *tp)
+{
+ if (tp->napi_stat == 1) {
+ napi_disable(&tp->napi);
+ tp->napi_stat--;
+ tp->napi_last_dis = (void *)_RET_IP_;
+ return;
+ }
+
+ WARN(1, "napi_stat=%d last_dis=%pF last_en=%pF\n",
+ tp->napi_stat, tp->napi_last_en, tp->napi_last_dis);
+}
+
static
int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
{
@@ -3787,7 +3814,6 @@ static bool rtl8153_in_nway(struct r8152
static void set_carrier(struct r8152 *tp)
{
struct net_device *netdev = tp->netdev;
- struct napi_struct *napi = &tp->napi;
u8 speed;
speed = rtl8152_get_speed(tp);
@@ -3796,12 +3822,12 @@ static void set_carrier(struct r8152 *tp
if (!netif_carrier_ok(netdev)) {
tp->rtl_ops.enable(tp);
netif_stop_queue(netdev);
- napi_disable(napi);
+ my_napi_disable(tp);
netif_carrier_on(netdev);
rtl_start_rx(tp);
clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
_rtl8152_set_rx_mode(netdev);
- napi_enable(&tp->napi);
+ my_napi_enable(tp);
netif_wake_queue(netdev);
netif_info(tp, link, netdev, "carrier on\n");
} else if (netif_queue_stopped(netdev) &&
@@ -3811,9 +3837,9 @@ static void set_carrier(struct r8152 *tp
} else {
if (netif_carrier_ok(netdev)) {
netif_carrier_off(netdev);
- napi_disable(napi);
+ my_napi_disable(tp);
tp->rtl_ops.disable(tp);
- napi_enable(napi);
+ my_napi_enable(tp);
netif_info(tp, link, netdev, "carrier off\n");
}
}
@@ -3934,7 +3960,7 @@ static int rtl8152_open(struct net_devic
res);
goto out_unlock;
}
- napi_enable(&tp->napi);
+ my_napi_enable(tp);
mutex_unlock(&tp->control);
@@ -3962,7 +3988,7 @@ static int rtl8152_close(struct net_devi
#ifdef CONFIG_PM_SLEEP
unregister_pm_notifier(&tp->pm_notifier);
#endif
- napi_disable(&tp->napi);
+ my_napi_disable(tp);
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
@@ -4230,7 +4256,7 @@ static int rtl8152_pre_reset(struct usb_
return 0;
netif_stop_queue(netdev);
- napi_disable(&tp->napi);
+ my_napi_disable(tp);
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule);
@@ -4264,7 +4290,7 @@ static int rtl8152_post_reset(struct usb
mutex_unlock(&tp->control);
}
- napi_enable(&tp->napi);
+ my_napi_enable(tp);
netif_wake_queue(netdev);
usb_submit_urb(tp->intr_urb, GFP_KERNEL);
@@ -4302,10 +4328,8 @@ static int rtl8152_runtime_resume(struct
struct net_device *netdev = tp->netdev;
if (netif_running(netdev) && netdev->flags & IFF_UP) {
- struct napi_struct *napi = &tp->napi;
-
tp->rtl_ops.autosuspend_en(tp, false);
- napi_disable(napi);
+ my_napi_disable(tp);
set_bit(WORK_ENABLE, &tp->flags);
if (netif_carrier_ok(netdev)) {
@@ -4318,7 +4342,7 @@ static int rtl8152_runtime_resume(struct
}
}
- napi_enable(napi);
+ my_napi_enable(tp);
clear_bit(SELECTIVE_SUSPEND, &tp->flags);
smp_mb__after_atomic();
@@ -4388,13 +4412,11 @@ static int rtl8152_runtime_suspend(struc
tp->rtl_ops.autosuspend_en(tp, true);
if (netif_carrier_ok(netdev)) {
- struct napi_struct *napi = &tp->napi;
-
- napi_disable(napi);
+ my_napi_disable(tp);
rtl_stop_rx(tp);
rxdy_gated_en(tp, false);
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
- napi_enable(napi);
+ my_napi_enable(tp);
}
if (delay_autosuspend(tp)) {
@@ -4415,14 +4437,12 @@ static int rtl8152_system_suspend(struct
netif_device_detach(netdev);
if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
- struct napi_struct *napi = &tp->napi;
-
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
- napi_disable(napi);
+ my_napi_disable(tp);
cancel_delayed_work_sync(&tp->schedule);
tp->rtl_ops.down(tp);
- napi_enable(napi);
+ my_napi_enable(tp);
}
return ret;
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5654,13 +5654,23 @@ EXPORT_SYMBOL(netif_napi_add);
void napi_disable(struct napi_struct *n)
{
+ int a;
+
might_sleep();
set_bit(NAPI_STATE_DISABLE, &n->state);
- while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
+ a = 0;
+ while (test_and_set_bit(NAPI_STATE_SCHED, &n->state)) {
msleep(1);
- while (test_and_set_bit(NAPI_STATE_NPSVC, &n->state))
+ if (WARN(a++ > 20 * 1000, "NAPI_STATE_SCHED never cleared"))
+ break;
+ }
+ a = 0;
+ while (test_and_set_bit(NAPI_STATE_NPSVC, &n->state)) {
msleep(1);
+ if (WARN(a++ > 20 * 1000, "NAPI_STATE_NPSVC never cleared"))
+ break;
+ }
hrtimer_cancel(&n->timer);
^ permalink raw reply
* Re: simplify procfs code for seq_file instances V3
From: Al Viro @ 2018-05-16 13:28 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Alexey Dobriyan, Greg Kroah-Hartman, Jiri Slaby,
Alessandro Zummo, Alexandre Belloni, linux-acpi, drbd-dev,
linux-ide, netdev, linux-rtc, megaraidlinux.pdl, linux-scsi,
devel, linux-afs, linux-ext4, jfs-discussion, netfilter-devel,
linux-kernel
In-Reply-To: <20180516094346.20506-1-hch@lst.de>
On Wed, May 16, 2018 at 11:43:04AM +0200, Christoph Hellwig wrote:
> We currently have hundreds of proc files that implement plain, read-only
> seq_file based interfaces. This series consolidates them using new
> procfs helpers that take the seq_operations or simple show callback
> directly.
>
> A git tree is available at:
>
> git://git.infradead.org/users/hch/misc.git proc_create.3
Pulled, but the last bit is a bleeding atrocity in need of followup
cleanup.
^ permalink raw reply
* Re: [PATCH 12/14] net: sched: retry action check-insert on concurrent modification
From: Jiri Pirko @ 2018-05-16 13:21 UTC (permalink / raw)
To: Vlad Buslov
Cc: netdev, davem, jhs, xiyou.wangcong, pablo, kadlec, fw, ast,
daniel, edumazet, keescook, linux-kernel, netfilter-devel,
coreteam, kliteyn
In-Reply-To: <vbfwow322k1.fsf@reg-r-vrt-018-180.mtr.labs.mlnx>
Wed, May 16, 2018 at 02:43:58PM CEST, vladbu@mellanox.com wrote:
>
>On Wed 16 May 2018 at 12:26, Jiri Pirko <jiri@resnulli.us> wrote:
>> Wed, May 16, 2018 at 01:55:06PM CEST, vladbu@mellanox.com wrote:
>>>
>>>On Wed 16 May 2018 at 09:59, Jiri Pirko <jiri@resnulli.us> wrote:
>>>> Mon, May 14, 2018 at 04:27:13PM CEST, vladbu@mellanox.com wrote:
>>>>>Retry check-insert sequence in action init functions if action with same
>>>>>index was inserted concurrently.
>>>>>
>>>>>Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
>>>>>---
>>>>> net/sched/act_bpf.c | 8 +++++++-
>>>>> net/sched/act_connmark.c | 8 +++++++-
>>>>> net/sched/act_csum.c | 8 +++++++-
>>>>> net/sched/act_gact.c | 8 +++++++-
>>>>> net/sched/act_ife.c | 8 +++++++-
>>>>> net/sched/act_ipt.c | 8 +++++++-
>>>>> net/sched/act_mirred.c | 8 +++++++-
>>>>> net/sched/act_nat.c | 8 +++++++-
>>>>> net/sched/act_pedit.c | 8 +++++++-
>>>>> net/sched/act_police.c | 9 ++++++++-
>>>>> net/sched/act_sample.c | 8 +++++++-
>>>>> net/sched/act_simple.c | 9 ++++++++-
>>>>> net/sched/act_skbedit.c | 8 +++++++-
>>>>> net/sched/act_skbmod.c | 8 +++++++-
>>>>> net/sched/act_tunnel_key.c | 9 ++++++++-
>>>>> net/sched/act_vlan.c | 9 ++++++++-
>>>>> 16 files changed, 116 insertions(+), 16 deletions(-)
>>>>>
>>>>>diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
>>>>>index 5554bf7..7e20fdc 100644
>>>>>--- a/net/sched/act_bpf.c
>>>>>+++ b/net/sched/act_bpf.c
>>>>>@@ -299,10 +299,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
>>>>>
>>>>> parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
>>>>>
>>>>>+replay:
>>>>> if (!tcf_idr_check(tn, parm->index, act, bind)) {
>>>>> ret = tcf_idr_create(tn, parm->index, est, act,
>>>>> &act_bpf_ops, bind, true);
>>>>>- if (ret < 0)
>>>>>+ /* Action with specified index was created concurrently.
>>>>>+ * Check again.
>>>>>+ */
>>>>>+ if (parm->index && ret == -ENOSPC)
>>>>>+ goto replay;
>>>>>+ else if (ret)
>>>>
>>>> Hmm, looks like you are doing the same/very similar thing in every act
>>>> code. I think it would make sense to introduce a helper function for
>>>> this purpose.
>>>
>>>This code uses goto so it can't be easily refactored into standalone
>>>function. Could you specify which part of this code you suggest to
>>>extract?
>>
>> Hmm, looking at the code, I think that what would help is to have a
>> helper that would atomically check if index exists and if not, it would
>> allocate one. Something like:
>>
>>
>> int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
>> struct tc_action **a, int bind)
>> {
>> struct tcf_idrinfo *idrinfo = tn->idrinfo;
>> struct tc_action *p;
>> int err;
>>
>> spin_lock(&idrinfo->lock);
>> if (*index) {
>> p = idr_find(&idrinfo->action_idr, *index);
>> if (p) {
>> if (bind)
>> p->tcfa_bindcnt++;
>> p->tcfa_refcnt++;
>> *a = p;
>> err = 0;
>> } else {
>> *a = NULL;
>> err = idr_alloc_u32(idr, NULL, index,
>> *index, GFP_ATOMIC);
>> }
>> } else {
>> *index = 1;
>> *a = NULL;
>> err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
>> }
>> spin_unlock(&idrinfo->lock);
>> return err;
>> }
>>
>> The act code would just check if "a" is NULL and if so, it would call
>> tcf_idr_create() with allocated index as arg.
>
>What about multiple actions that have arbitrary code between initial
>check and idr allocation that is currently inside tcf_idr_create()?
Why it would be a problem to have them after the allocation?
There is one issue though with my draft. tcf_idr_insert() function
which actually assigns a "p" pointer to the idr index is called later on.
Until that happens, the idr_find() would return NULL even if the index
is actually allocated. We cannot assign "p" in tcf_idr_check_alloc()
because it is allocated only later on in tcf_idr_create(). But that is
resolvable by the following trick:
int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
struct tc_action **a, int bind)
{
struct tcf_idrinfo *idrinfo = tn->idrinfo;
struct tc_action *p;
int err;
again:
spin_lock(&idrinfo->lock);
if (*index) {
p = idr_find(&idrinfo->action_idr, *index);
if (IS_ERR(p)) {
/* This means that another process allocated
* index but did not assign the pointer yet.
*/
spin_unlock(&idrinfo->lock);
goto again;
}
if (p) {
if (bind)
p->tcfa_bindcnt++;
p->tcfa_refcnt++;
*a = p;
err = 0;
} else {
*a = NULL;
err = idr_alloc_u32(idr, NULL, index,
*index, GFP_ATOMIC);
idr_replace(&idrinfo->action_idr,
ERR_PTR(-EBUSY), *index);
}
} else {
*index = 1;
*a = NULL;
err = idr_alloc_u32(idr, NULL, index, UINT_MAX, GFP_ATOMIC);
idr_replace(&idrinfo->action_idr, ERR_PTR(-EBUSY), *index);
}
spin_unlock(&idrinfo->lock);
return err;
}
>
>>
>>
>>>
>>>>
>>>> [...]
>>>
>
^ permalink raw reply
* Re: i.MX6S/DL and QCA8334 switch using DSA driver - CPU port not working
From: Andrew Lunn @ 2018-05-16 13:17 UTC (permalink / raw)
To: Michal Vokáč; +Cc: Florian Fainelli, netdev, Vivien Didelot
In-Reply-To: <bc6fbc83-7c12-182b-cc8c-0dd0da6a9e0e@gmail.com>
> So as the fixed-link subnode is optional we still should force some sensible
> defaults if it is not used. Same as Marvell drives does. Can I say that we
> agreed that the current CPU port setting is not correct and the fastest link
> speed and duplex supported by the chip should be set as a default?
That is a sensible default.
> >As far as people using this driver, John submitted it, but we have not
> >see many fixes/enhancements, so I am not clear who is actually using it
> >these days... glad you are though!
>
> Thanks.
>
> To the compatibility of the driver with the QCA8334 that I use. I am not sure
> what should be the correct way to deal with that. For example Marvell binding
> uses only two compatible strings for a large range of chips in one family.
> While Broadcom has one compatible string for each chip.
With the Marvell devices, there is an ID register which tells you the
model. The compatible string tells us the information we need in order
to find that ID register. Once we know the ID, we have all the
information we need, so don't need a more specific compatible string.
> As I mentioned earlier
> in this thread the QCA8334 switch has four ports while the already supported
> QCA8337 has seven ports. That is the only difference. Register address space
> is the same.
Can you identify the exact model using some ID register? If yes, than
the existing compatible string is sufficient. If no, you need an
additional compatible string.
Andrew
^ permalink raw reply
* Re: [Jfs-discussion] [PATCH 25/42] jfs: simplify procfs code
From: Dave Kleikamp @ 2018-05-16 13:15 UTC (permalink / raw)
To: Christoph Hellwig, Andrew Morton, Alexander Viro
Cc: linux-rtc, Alessandro Zummo, Alexandre Belloni, devel, linux-scsi,
linux-acpi, Greg Kroah-Hartman, Jiri Slaby, linux-kernel,
Alexey Dobriyan, linux-ide, netfilter-devel, megaraidlinux.pdl,
netdev, linux-ext4, linux-afs, jfs-discussion, drbd-dev
In-Reply-To: <20180516094346.20506-26-hch@lst.de>
On 05/16/2018 04:43 AM, Christoph Hellwig wrote:
> Use remove_proc_subtree to remove the whole subtree on cleanup, and
> unwind the registration loop into individual calls. Switch to use
> proc_create_seq where applicable.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Dave Kleikamp <dave.kleikamp@oracle.com>
> ---
> fs/jfs/jfs_debug.c | 43 ++++++++++++++-----------------------------
> fs/jfs/jfs_debug.h | 10 +++++-----
> fs/jfs/jfs_logmgr.c | 14 +-------------
> fs/jfs/jfs_metapage.c | 14 +-------------
> fs/jfs/jfs_txnmgr.c | 28 ++--------------------------
> fs/jfs/jfs_xtree.c | 14 +-------------
> 6 files changed, 24 insertions(+), 99 deletions(-)
>
> diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
> index a70907606025..35a5b2a81ae0 100644
> --- a/fs/jfs/jfs_debug.c
> +++ b/fs/jfs/jfs_debug.c
> @@ -29,7 +29,6 @@
>
> #ifdef PROC_FS_JFS /* see jfs_debug.h */
>
> -static struct proc_dir_entry *base;
> #ifdef CONFIG_JFS_DEBUG
> static int jfs_loglevel_proc_show(struct seq_file *m, void *v)
> {
> @@ -66,43 +65,29 @@ static const struct file_operations jfs_loglevel_proc_fops = {
> };
> #endif
>
> -static struct {
> - const char *name;
> - const struct file_operations *proc_fops;
> -} Entries[] = {
> -#ifdef CONFIG_JFS_STATISTICS
> - { "lmstats", &jfs_lmstats_proc_fops, },
> - { "txstats", &jfs_txstats_proc_fops, },
> - { "xtstat", &jfs_xtstat_proc_fops, },
> - { "mpstat", &jfs_mpstat_proc_fops, },
> -#endif
> -#ifdef CONFIG_JFS_DEBUG
> - { "TxAnchor", &jfs_txanchor_proc_fops, },
> - { "loglevel", &jfs_loglevel_proc_fops }
> -#endif
> -};
> -#define NPROCENT ARRAY_SIZE(Entries)
> -
> void jfs_proc_init(void)
> {
> - int i;
> + struct proc_dir_entry *base;
>
> - if (!(base = proc_mkdir("fs/jfs", NULL)))
> + base = proc_mkdir("fs/jfs", NULL);
> + if (!base)
> return;
>
> - for (i = 0; i < NPROCENT; i++)
> - proc_create(Entries[i].name, 0, base, Entries[i].proc_fops);
> +#ifdef CONFIG_JFS_STATISTICS
> + proc_create_single("lmstats", 0, base, jfs_lmstats_proc_show);
> + proc_create_single("txstats", 0, base, jfs_txstats_proc_show);
> + proc_create_single("xtstat", 0, base, jfs_xtstat_proc_show);
> + proc_create_single("mpstat", 0, base, jfs_mpstat_proc_show);
> +#endif
> +#ifdef CONFIG_JFS_DEBUG
> + proc_create_single("TxAnchor", 0, base, jfs_txanchor_proc_show);
> + proc_create("loglevel", 0, base, &jfs_loglevel_proc_fops);
> +#endif
> }
>
> void jfs_proc_clean(void)
> {
> - int i;
> -
> - if (base) {
> - for (i = 0; i < NPROCENT; i++)
> - remove_proc_entry(Entries[i].name, base);
> - remove_proc_entry("fs/jfs", NULL);
> - }
> + remove_proc_subtree("fs/jfs", NULL);
> }
>
> #endif /* PROC_FS_JFS */
> diff --git a/fs/jfs/jfs_debug.h b/fs/jfs/jfs_debug.h
> index eafd1300a00b..0d9e35da8462 100644
> --- a/fs/jfs/jfs_debug.h
> +++ b/fs/jfs/jfs_debug.h
> @@ -62,7 +62,7 @@ extern void jfs_proc_clean(void);
>
> extern int jfsloglevel;
>
> -extern const struct file_operations jfs_txanchor_proc_fops;
> +int jfs_txanchor_proc_show(struct seq_file *m, void *v);
>
> /* information message: e.g., configuration, major event */
> #define jfs_info(fmt, arg...) do { \
> @@ -105,10 +105,10 @@ extern const struct file_operations jfs_txanchor_proc_fops;
> * ----------
> */
> #ifdef CONFIG_JFS_STATISTICS
> -extern const struct file_operations jfs_lmstats_proc_fops;
> -extern const struct file_operations jfs_txstats_proc_fops;
> -extern const struct file_operations jfs_mpstat_proc_fops;
> -extern const struct file_operations jfs_xtstat_proc_fops;
> +int jfs_lmstats_proc_show(struct seq_file *m, void *v);
> +int jfs_txstats_proc_show(struct seq_file *m, void *v);
> +int jfs_mpstat_proc_show(struct seq_file *m, void *v);
> +int jfs_xtstat_proc_show(struct seq_file *m, void *v);
>
> #define INCREMENT(x) ((x)++)
> #define DECREMENT(x) ((x)--)
> diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
> index 0e5d412c0b01..6b68df395892 100644
> --- a/fs/jfs/jfs_logmgr.c
> +++ b/fs/jfs/jfs_logmgr.c
> @@ -2493,7 +2493,7 @@ int lmLogFormat(struct jfs_log *log, s64 logAddress, int logSize)
> }
>
> #ifdef CONFIG_JFS_STATISTICS
> -static int jfs_lmstats_proc_show(struct seq_file *m, void *v)
> +int jfs_lmstats_proc_show(struct seq_file *m, void *v)
> {
> seq_printf(m,
> "JFS Logmgr stats\n"
> @@ -2510,16 +2510,4 @@ static int jfs_lmstats_proc_show(struct seq_file *m, void *v)
> lmStat.partial_page);
> return 0;
> }
> -
> -static int jfs_lmstats_proc_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, jfs_lmstats_proc_show, NULL);
> -}
> -
> -const struct file_operations jfs_lmstats_proc_fops = {
> - .open = jfs_lmstats_proc_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> #endif /* CONFIG_JFS_STATISTICS */
> diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
> index 1a3b0cc22ad3..fa2c6824c7f2 100644
> --- a/fs/jfs/jfs_metapage.c
> +++ b/fs/jfs/jfs_metapage.c
> @@ -815,7 +815,7 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len)
> }
>
> #ifdef CONFIG_JFS_STATISTICS
> -static int jfs_mpstat_proc_show(struct seq_file *m, void *v)
> +int jfs_mpstat_proc_show(struct seq_file *m, void *v)
> {
> seq_printf(m,
> "JFS Metapage statistics\n"
> @@ -828,16 +828,4 @@ static int jfs_mpstat_proc_show(struct seq_file *m, void *v)
> mpStat.lockwait);
> return 0;
> }
> -
> -static int jfs_mpstat_proc_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, jfs_mpstat_proc_show, NULL);
> -}
> -
> -const struct file_operations jfs_mpstat_proc_fops = {
> - .open = jfs_mpstat_proc_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> #endif
> diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
> index 4d973524c887..a5663cb621d8 100644
> --- a/fs/jfs/jfs_txnmgr.c
> +++ b/fs/jfs/jfs_txnmgr.c
> @@ -2998,7 +2998,7 @@ int jfs_sync(void *arg)
> }
>
> #if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_DEBUG)
> -static int jfs_txanchor_proc_show(struct seq_file *m, void *v)
> +int jfs_txanchor_proc_show(struct seq_file *m, void *v)
> {
> char *freewait;
> char *freelockwait;
> @@ -3032,22 +3032,10 @@ static int jfs_txanchor_proc_show(struct seq_file *m, void *v)
> list_empty(&TxAnchor.unlock_queue) ? "" : "not ");
> return 0;
> }
> -
> -static int jfs_txanchor_proc_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, jfs_txanchor_proc_show, NULL);
> -}
> -
> -const struct file_operations jfs_txanchor_proc_fops = {
> - .open = jfs_txanchor_proc_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> #endif
>
> #if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_STATISTICS)
> -static int jfs_txstats_proc_show(struct seq_file *m, void *v)
> +int jfs_txstats_proc_show(struct seq_file *m, void *v)
> {
> seq_printf(m,
> "JFS TxStats\n"
> @@ -3072,16 +3060,4 @@ static int jfs_txstats_proc_show(struct seq_file *m, void *v)
> TxStat.txLockAlloc_freelock);
> return 0;
> }
> -
> -static int jfs_txstats_proc_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, jfs_txstats_proc_show, NULL);
> -}
> -
> -const struct file_operations jfs_txstats_proc_fops = {
> - .open = jfs_txstats_proc_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> #endif
> diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
> index 5cde6d2fcfca..2c200b5256a6 100644
> --- a/fs/jfs/jfs_xtree.c
> +++ b/fs/jfs/jfs_xtree.c
> @@ -3874,7 +3874,7 @@ s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size)
> }
>
> #ifdef CONFIG_JFS_STATISTICS
> -static int jfs_xtstat_proc_show(struct seq_file *m, void *v)
> +int jfs_xtstat_proc_show(struct seq_file *m, void *v)
> {
> seq_printf(m,
> "JFS Xtree statistics\n"
> @@ -3887,16 +3887,4 @@ static int jfs_xtstat_proc_show(struct seq_file *m, void *v)
> xtStat.split);
> return 0;
> }
> -
> -static int jfs_xtstat_proc_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, jfs_xtstat_proc_show, NULL);
> -}
> -
> -const struct file_operations jfs_xtstat_proc_fops = {
> - .open = jfs_xtstat_proc_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> #endif
>
^ permalink raw reply
* Re: Xilinx axienet + DP83620 in fiber mode won't set netif_carrier_on
From: Andrew Lunn @ 2018-05-16 13:11 UTC (permalink / raw)
To: Alvaro G. M.; +Cc: netdev
In-Reply-To: <20180516091630.GA17469@salem.gmr.ssr.upm.es>
On Wed, May 16, 2018 at 11:16:31AM +0200, Alvaro G. M. wrote:
> Hi,
>
> I have a custom board with a Xilinx FPGA running Microblaze and fitting a
> Xilinx Axi Ethernet IP core. This core communicates through MII mode with a
> DP83620 PHY from Texas that supports both cabled and fiber interfaces, of
> which I'm using the latter.
>
> Under these circumstances, I've noticed that the interface is pretty much
> dead except for receiving broadcast packages, so I tried to dig on the
> driver to find the cause. Please, beware that I'm not very familiar with the
> netdev subsystem, so I may be mistaken on lots of things.
>
> It seems that of_phy_connect ends up calling netif_carrier_off:
>
> phy_device.c:1036
> /* Initial carrier state is off as the phy is about to be
> * (re)initialized.
> */
> netif_carrier_off(phydev->attached_dev);
>
> /* Do initial configuration here, now that
> * we have certain key parameters
> * (dev_flags and interface)
> */
> err = phy_init_hw(phydev);
> if (err)
> goto error;
>
> phy_resume(phydev);
>
> However, neither xilinx_axienet_main.c nor dp83848.c ever runs
> netif_carrier_on.
Hi Alvaro
What should happen in general terms is that at some point the link to
the peer is established. phylib, the generic PHY code, polls the PHY
ever second, asking what the link state is. When the link changes from
down to up, phylib will call the link_adjust callback in the MAC, and
netif_carrier_on().
When the PHY reports the link has gone down, it does similar, calls
the adjust_link callback, and netif_carrier_off().
So what you need to do is find out why the PHY driver never reports
link up. Does the PHY even know when the link is up? Often SFF/SFP
modules have a Signal Detect pin, which is connected to a gpio. Do you
have something like that? If so, you should look at the PHYLINK code
and the SFP device which was added recently.
Andrew
^ permalink raw reply
* [PATCH net-next V2 2/2] cxgb4: Support ethtool private flags
From: Ganesh Goudar @ 2018-05-16 13:11 UTC (permalink / raw)
To: netdev, davem
Cc: nirranjan, indranil, venkatesh, Arjun Vynipadath, Casey Leedom,
Ganesh Goudar
From: Arjun Vynipadath <arjun@chelsio.com>
This is used to change TX workrequests, which helps in
host->vf communication.
Signed-off-by: Arjun Vynipadath <arjun@chelsio.com>
Signed-off-by: Casey Leedom <leedom@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
---
V2: no v2 changes in this patch
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
index 59d04d7..1d979a6 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
@@ -177,6 +177,10 @@ static char loopback_stats_strings[][ETH_GSTRING_LEN] = {
"bg3_frames_trunc ",
};
+static const char cxgb4_priv_flags_strings[][ETH_GSTRING_LEN] = {
+ [PRIV_FLAG_PORT_TX_VM_BIT] = "port_tx_vm_wr",
+};
+
static int get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
@@ -185,6 +189,8 @@ static int get_sset_count(struct net_device *dev, int sset)
ARRAY_SIZE(adapter_stats_strings) +
ARRAY_SIZE(channel_stats_strings) +
ARRAY_SIZE(loopback_stats_strings);
+ case ETH_SS_PRIV_FLAGS:
+ return ARRAY_SIZE(cxgb4_priv_flags_strings);
default:
return -EOPNOTSUPP;
}
@@ -235,6 +241,7 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
FW_HDR_FW_VER_MINOR_G(exprom_vers),
FW_HDR_FW_VER_MICRO_G(exprom_vers),
FW_HDR_FW_VER_BUILD_G(exprom_vers));
+ info->n_priv_flags = ARRAY_SIZE(cxgb4_priv_flags_strings);
}
static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
@@ -250,6 +257,9 @@ static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
data += sizeof(channel_stats_strings);
memcpy(data, loopback_stats_strings,
sizeof(loopback_stats_strings));
+ } else if (stringset == ETH_SS_PRIV_FLAGS) {
+ memcpy(data, cxgb4_priv_flags_strings,
+ sizeof(cxgb4_priv_flags_strings));
}
}
@@ -1503,6 +1513,36 @@ static int cxgb4_get_module_eeprom(struct net_device *dev,
offset, len, &data[eprom->len - len]);
}
+static u32 cxgb4_get_priv_flags(struct net_device *netdev)
+{
+ struct port_info *pi = netdev_priv(netdev);
+ struct adapter *adapter = pi->adapter;
+
+ return (adapter->eth_flags | pi->eth_flags);
+}
+
+/**
+ * set_flags - set/unset specified flags if passed in new_flags
+ * @cur_flags: pointer to current flags
+ * @new_flags: new incoming flags
+ * @flags: set of flags to set/unset
+ */
+static inline void set_flags(u32 *cur_flags, u32 new_flags, u32 flags)
+{
+ *cur_flags = (*cur_flags & ~flags) | (new_flags & flags);
+}
+
+static int cxgb4_set_priv_flags(struct net_device *netdev, u32 flags)
+{
+ struct port_info *pi = netdev_priv(netdev);
+ struct adapter *adapter = pi->adapter;
+
+ set_flags(&adapter->eth_flags, flags, PRIV_FLAGS_ADAP);
+ set_flags(&pi->eth_flags, flags, PRIV_FLAGS_PORT);
+
+ return 0;
+}
+
static const struct ethtool_ops cxgb_ethtool_ops = {
.get_link_ksettings = get_link_ksettings,
.set_link_ksettings = set_link_ksettings,
@@ -1539,6 +1579,8 @@ static const struct ethtool_ops cxgb_ethtool_ops = {
.get_dump_data = get_dump_data,
.get_module_info = cxgb4_get_module_info,
.get_module_eeprom = cxgb4_get_module_eeprom,
+ .get_priv_flags = cxgb4_get_priv_flags,
+ .set_priv_flags = cxgb4_set_priv_flags,
};
void cxgb4_set_ethtool_ops(struct net_device *netdev)
--
2.1.0
^ permalink raw reply related
* [PATCH net-next V2 1/2] cxgb4: Add support for FW_ETH_TX_PKT_VM_WR
From: Ganesh Goudar @ 2018-05-16 13:10 UTC (permalink / raw)
To: netdev, davem
Cc: nirranjan, indranil, venkatesh, Arjun Vynipadath, Casey Leedom,
Ganesh Goudar
From: Arjun Vynipadath <arjun@chelsio.com>
The present TX workrequest(FW_ETH_TX_PKT_WR) cant be used for
host->vf communication, since it doesn't loopback the outgoing
packets to virtual interfaces on the same port. This can be done using
FW_ETH_TX_PKT_VM_WR.
This fix depends on ethtool_flags to determine what WR to use for
TX path. Support for setting this flags by user is added in next commit.
Based on the original work by : Casey Leedom <leedom@chelsio.com>
Signed-off-by: Casey Leedom <leedom@chelsio.com>
Signed-off-by: Arjun Vynipadath <arjun@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
---
V2: rename t4_eth_xmit() and t4vf_eth_xmit() to fix build error
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 13 +-
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +-
drivers/net/ethernet/chelsio/cxgb4/sge.c | 372 +++++++++++++++++++++++-
3 files changed, 383 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 211086b..e7dd49d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -518,6 +518,15 @@ enum {
MAX_INGQ = MAX_ETH_QSETS + INGQ_EXTRAS,
};
+enum {
+ PRIV_FLAG_PORT_TX_VM_BIT,
+};
+
+#define PRIV_FLAG_PORT_TX_VM BIT(PRIV_FLAG_PORT_TX_VM_BIT)
+
+#define PRIV_FLAGS_ADAP 0
+#define PRIV_FLAGS_PORT PRIV_FLAG_PORT_TX_VM
+
struct adapter;
struct sge_rspq;
@@ -554,6 +563,7 @@ struct port_info {
struct hwtstamp_config tstamp_config;
bool ptp_enable;
struct sched_table *sched_tbl;
+ u32 eth_flags;
};
struct dentry;
@@ -864,6 +874,7 @@ struct adapter {
unsigned int flags;
unsigned int adap_idx;
enum chip_type chip;
+ unsigned int eth_flags;
int msg_enable;
__be16 vxlan_port;
@@ -1330,7 +1341,7 @@ void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
void t4_free_sge_resources(struct adapter *adap);
void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q);
irq_handler_t t4_intr_handler(struct adapter *adap);
-netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t t4_start_xmit(struct sk_buff *skb, struct net_device *dev);
int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
const struct pkt_gl *gl);
int t4_mgmt_tx(struct adapter *adap, struct sk_buff *skb);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 130d1ee..2942b30 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3211,7 +3211,7 @@ static netdev_features_t cxgb_fix_features(struct net_device *dev,
static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_open = cxgb_open,
.ndo_stop = cxgb_close,
- .ndo_start_xmit = t4_eth_xmit,
+ .ndo_start_xmit = t4_start_xmit,
.ndo_select_queue = cxgb_select_queue,
.ndo_get_stats64 = cxgb_get_stats,
.ndo_set_rx_mode = cxgb_set_rxmode,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 0f87e97..ede1ebc 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -1289,13 +1289,13 @@ static inline void t6_fill_tnl_lso(struct sk_buff *skb,
}
/**
- * t4_eth_xmit - add a packet to an Ethernet Tx queue
+ * cxgb4_eth_xmit - add a packet to an Ethernet Tx queue
* @skb: the packet
* @dev: the egress net device
*
* Add a packet to an SGE Ethernet Tx queue. Runs with softirqs disabled.
*/
-netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t cxgb4_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
u32 wr_mid, ctrl0, op;
u64 cntrl, *end, *sgl;
@@ -1549,6 +1549,374 @@ out_free: dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
+/* Constants ... */
+enum {
+ /* Egress Queue sizes, producer and consumer indices are all in units
+ * of Egress Context Units bytes. Note that as far as the hardware is
+ * concerned, the free list is an Egress Queue (the host produces free
+ * buffers which the hardware consumes) and free list entries are
+ * 64-bit PCI DMA addresses.
+ */
+ EQ_UNIT = SGE_EQ_IDXSIZE,
+ FL_PER_EQ_UNIT = EQ_UNIT / sizeof(__be64),
+ TXD_PER_EQ_UNIT = EQ_UNIT / sizeof(__be64),
+
+ T4VF_ETHTXQ_MAX_HDR = (sizeof(struct fw_eth_tx_pkt_vm_wr) +
+ sizeof(struct cpl_tx_pkt_lso_core) +
+ sizeof(struct cpl_tx_pkt_core)) / sizeof(__be64),
+};
+
+/**
+ * t4vf_is_eth_imm - can an Ethernet packet be sent as immediate data?
+ * @skb: the packet
+ *
+ * Returns whether an Ethernet packet is small enough to fit completely as
+ * immediate data.
+ */
+static inline int t4vf_is_eth_imm(const struct sk_buff *skb)
+{
+ /* The VF Driver uses the FW_ETH_TX_PKT_VM_WR firmware Work Request
+ * which does not accommodate immediate data. We could dike out all
+ * of the support code for immediate data but that would tie our hands
+ * too much if we ever want to enhace the firmware. It would also
+ * create more differences between the PF and VF Drivers.
+ */
+ return false;
+}
+
+/**
+ * t4vf_calc_tx_flits - calculate the number of flits for a packet TX WR
+ * @skb: the packet
+ *
+ * Returns the number of flits needed for a TX Work Request for the
+ * given Ethernet packet, including the needed WR and CPL headers.
+ */
+static inline unsigned int t4vf_calc_tx_flits(const struct sk_buff *skb)
+{
+ unsigned int flits;
+
+ /* If the skb is small enough, we can pump it out as a work request
+ * with only immediate data. In that case we just have to have the
+ * TX Packet header plus the skb data in the Work Request.
+ */
+ if (t4vf_is_eth_imm(skb))
+ return DIV_ROUND_UP(skb->len + sizeof(struct cpl_tx_pkt),
+ sizeof(__be64));
+
+ /* Otherwise, we're going to have to construct a Scatter gather list
+ * of the skb body and fragments. We also include the flits necessary
+ * for the TX Packet Work Request and CPL. We always have a firmware
+ * Write Header (incorporated as part of the cpl_tx_pkt_lso and
+ * cpl_tx_pkt structures), followed by either a TX Packet Write CPL
+ * message or, if we're doing a Large Send Offload, an LSO CPL message
+ * with an embedded TX Packet Write CPL message.
+ */
+ flits = sgl_len(skb_shinfo(skb)->nr_frags + 1);
+ if (skb_shinfo(skb)->gso_size)
+ flits += (sizeof(struct fw_eth_tx_pkt_vm_wr) +
+ sizeof(struct cpl_tx_pkt_lso_core) +
+ sizeof(struct cpl_tx_pkt_core)) / sizeof(__be64);
+ else
+ flits += (sizeof(struct fw_eth_tx_pkt_vm_wr) +
+ sizeof(struct cpl_tx_pkt_core)) / sizeof(__be64);
+ return flits;
+}
+
+/**
+ * cxgb4_vf_eth_xmit - add a packet to an Ethernet TX queue
+ * @skb: the packet
+ * @dev: the egress net device
+ *
+ * Add a packet to an SGE Ethernet TX queue. Runs with softirqs disabled.
+ */
+static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u32 wr_mid;
+ u64 cntrl, *end;
+ int qidx, credits, max_pkt_len;
+ unsigned int flits, ndesc;
+ struct adapter *adapter;
+ struct sge_eth_txq *txq;
+ const struct port_info *pi;
+ struct fw_eth_tx_pkt_vm_wr *wr;
+ struct cpl_tx_pkt_core *cpl;
+ const struct skb_shared_info *ssi;
+ dma_addr_t addr[MAX_SKB_FRAGS + 1];
+ const size_t fw_hdr_copy_len = (sizeof(wr->ethmacdst) +
+ sizeof(wr->ethmacsrc) +
+ sizeof(wr->ethtype) +
+ sizeof(wr->vlantci));
+
+ /* The chip minimum packet length is 10 octets but the firmware
+ * command that we are using requires that we copy the Ethernet header
+ * (including the VLAN tag) into the header so we reject anything
+ * smaller than that ...
+ */
+ if (unlikely(skb->len < fw_hdr_copy_len))
+ goto out_free;
+
+ /* Discard the packet if the length is greater than mtu */
+ max_pkt_len = ETH_HLEN + dev->mtu;
+ if (skb_vlan_tag_present(skb))
+ max_pkt_len += VLAN_HLEN;
+ if (!skb_shinfo(skb)->gso_size && (unlikely(skb->len > max_pkt_len)))
+ goto out_free;
+
+ /* Figure out which TX Queue we're going to use. */
+ pi = netdev_priv(dev);
+ adapter = pi->adapter;
+ qidx = skb_get_queue_mapping(skb);
+ WARN_ON(qidx >= pi->nqsets);
+ txq = &adapter->sge.ethtxq[pi->first_qset + qidx];
+
+ /* Take this opportunity to reclaim any TX Descriptors whose DMA
+ * transfers have completed.
+ */
+ cxgb4_reclaim_completed_tx(adapter, &txq->q, true);
+
+ /* Calculate the number of flits and TX Descriptors we're going to
+ * need along with how many TX Descriptors will be left over after
+ * we inject our Work Request.
+ */
+ flits = t4vf_calc_tx_flits(skb);
+ ndesc = flits_to_desc(flits);
+ credits = txq_avail(&txq->q) - ndesc;
+
+ if (unlikely(credits < 0)) {
+ /* Not enough room for this packet's Work Request. Stop the
+ * TX Queue and return a "busy" condition. The queue will get
+ * started later on when the firmware informs us that space
+ * has opened up.
+ */
+ eth_txq_stop(txq);
+ dev_err(adapter->pdev_dev,
+ "%s: TX ring %u full while queue awake!\n",
+ dev->name, qidx);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (!t4vf_is_eth_imm(skb) &&
+ unlikely(cxgb4_map_skb(adapter->pdev_dev, skb, addr) < 0)) {
+ /* We need to map the skb into PCI DMA space (because it can't
+ * be in-lined directly into the Work Request) and the mapping
+ * operation failed. Record the error and drop the packet.
+ */
+ txq->mapping_err++;
+ goto out_free;
+ }
+
+ wr_mid = FW_WR_LEN16_V(DIV_ROUND_UP(flits, 2));
+ if (unlikely(credits < ETHTXQ_STOP_THRES)) {
+ /* After we're done injecting the Work Request for this
+ * packet, we'll be below our "stop threshold" so stop the TX
+ * Queue now and schedule a request for an SGE Egress Queue
+ * Update message. The queue will get started later on when
+ * the firmware processes this Work Request and sends us an
+ * Egress Queue Status Update message indicating that space
+ * has opened up.
+ */
+ eth_txq_stop(txq);
+ wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
+ }
+
+ /* Start filling in our Work Request. Note that we do _not_ handle
+ * the WR Header wrapping around the TX Descriptor Ring. If our
+ * maximum header size ever exceeds one TX Descriptor, we'll need to
+ * do something else here.
+ */
+ WARN_ON(DIV_ROUND_UP(T4VF_ETHTXQ_MAX_HDR, TXD_PER_EQ_UNIT) > 1);
+ wr = (void *)&txq->q.desc[txq->q.pidx];
+ wr->equiq_to_len16 = cpu_to_be32(wr_mid);
+ wr->r3[0] = cpu_to_be32(0);
+ wr->r3[1] = cpu_to_be32(0);
+ skb_copy_from_linear_data(skb, (void *)wr->ethmacdst, fw_hdr_copy_len);
+ end = (u64 *)wr + flits;
+
+ /* If this is a Large Send Offload packet we'll put in an LSO CPL
+ * message with an encapsulated TX Packet CPL message. Otherwise we
+ * just use a TX Packet CPL message.
+ */
+ ssi = skb_shinfo(skb);
+ if (ssi->gso_size) {
+ struct cpl_tx_pkt_lso_core *lso = (void *)(wr + 1);
+ bool v6 = (ssi->gso_type & SKB_GSO_TCPV6) != 0;
+ int l3hdr_len = skb_network_header_len(skb);
+ int eth_xtra_len = skb_network_offset(skb) - ETH_HLEN;
+
+ wr->op_immdlen =
+ cpu_to_be32(FW_WR_OP_V(FW_ETH_TX_PKT_VM_WR) |
+ FW_WR_IMMDLEN_V(sizeof(*lso) +
+ sizeof(*cpl)));
+ /* Fill in the LSO CPL message. */
+ lso->lso_ctrl =
+ cpu_to_be32(LSO_OPCODE_V(CPL_TX_PKT_LSO) |
+ LSO_FIRST_SLICE_F |
+ LSO_LAST_SLICE_F |
+ LSO_IPV6_V(v6) |
+ LSO_ETHHDR_LEN_V(eth_xtra_len / 4) |
+ LSO_IPHDR_LEN_V(l3hdr_len / 4) |
+ LSO_TCPHDR_LEN_V(tcp_hdr(skb)->doff));
+ lso->ipid_ofst = cpu_to_be16(0);
+ lso->mss = cpu_to_be16(ssi->gso_size);
+ lso->seqno_offset = cpu_to_be32(0);
+ if (is_t4(adapter->params.chip))
+ lso->len = cpu_to_be32(skb->len);
+ else
+ lso->len = cpu_to_be32(LSO_T5_XFER_SIZE_V(skb->len));
+
+ /* Set up TX Packet CPL pointer, control word and perform
+ * accounting.
+ */
+ cpl = (void *)(lso + 1);
+
+ if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+ cntrl = TXPKT_ETHHDR_LEN_V(eth_xtra_len);
+ else
+ cntrl = T6_TXPKT_ETHHDR_LEN_V(eth_xtra_len);
+
+ cntrl |= TXPKT_CSUM_TYPE_V(v6 ?
+ TX_CSUM_TCPIP6 : TX_CSUM_TCPIP) |
+ TXPKT_IPHDR_LEN_V(l3hdr_len);
+ txq->tso++;
+ txq->tx_cso += ssi->gso_segs;
+ } else {
+ int len;
+
+ len = (t4vf_is_eth_imm(skb)
+ ? skb->len + sizeof(*cpl)
+ : sizeof(*cpl));
+ wr->op_immdlen =
+ cpu_to_be32(FW_WR_OP_V(FW_ETH_TX_PKT_VM_WR) |
+ FW_WR_IMMDLEN_V(len));
+
+ /* Set up TX Packet CPL pointer, control word and perform
+ * accounting.
+ */
+ cpl = (void *)(wr + 1);
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ cntrl = hwcsum(adapter->params.chip, skb) |
+ TXPKT_IPCSUM_DIS_F;
+ txq->tx_cso++;
+ } else {
+ cntrl = TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F;
+ }
+ }
+
+ /* If there's a VLAN tag present, add that to the list of things to
+ * do in this Work Request.
+ */
+ if (skb_vlan_tag_present(skb)) {
+ txq->vlan_ins++;
+ cntrl |= TXPKT_VLAN_VLD_F | TXPKT_VLAN_V(skb_vlan_tag_get(skb));
+ }
+
+ /* Fill in the TX Packet CPL message header. */
+ cpl->ctrl0 = cpu_to_be32(TXPKT_OPCODE_V(CPL_TX_PKT_XT) |
+ TXPKT_INTF_V(pi->port_id) |
+ TXPKT_PF_V(0));
+ cpl->pack = cpu_to_be16(0);
+ cpl->len = cpu_to_be16(skb->len);
+ cpl->ctrl1 = cpu_to_be64(cntrl);
+
+ /* Fill in the body of the TX Packet CPL message with either in-lined
+ * data or a Scatter/Gather List.
+ */
+ if (t4vf_is_eth_imm(skb)) {
+ /* In-line the packet's data and free the skb since we don't
+ * need it any longer.
+ */
+ cxgb4_inline_tx_skb(skb, &txq->q, cpl + 1);
+ dev_consume_skb_any(skb);
+ } else {
+ /* Write the skb's Scatter/Gather list into the TX Packet CPL
+ * message and retain a pointer to the skb so we can free it
+ * later when its DMA completes. (We store the skb pointer
+ * in the Software Descriptor corresponding to the last TX
+ * Descriptor used by the Work Request.)
+ *
+ * The retained skb will be freed when the corresponding TX
+ * Descriptors are reclaimed after their DMAs complete.
+ * However, this could take quite a while since, in general,
+ * the hardware is set up to be lazy about sending DMA
+ * completion notifications to us and we mostly perform TX
+ * reclaims in the transmit routine.
+ *
+ * This is good for performamce but means that we rely on new
+ * TX packets arriving to run the destructors of completed
+ * packets, which open up space in their sockets' send queues.
+ * Sometimes we do not get such new packets causing TX to
+ * stall. A single UDP transmitter is a good example of this
+ * situation. We have a clean up timer that periodically
+ * reclaims completed packets but it doesn't run often enough
+ * (nor do we want it to) to prevent lengthy stalls. A
+ * solution to this problem is to run the destructor early,
+ * after the packet is queued but before it's DMAd. A con is
+ * that we lie to socket memory accounting, but the amount of
+ * extra memory is reasonable (limited by the number of TX
+ * descriptors), the packets do actually get freed quickly by
+ * new packets almost always, and for protocols like TCP that
+ * wait for acks to really free up the data the extra memory
+ * is even less. On the positive side we run the destructors
+ * on the sending CPU rather than on a potentially different
+ * completing CPU, usually a good thing.
+ *
+ * Run the destructor before telling the DMA engine about the
+ * packet to make sure it doesn't complete and get freed
+ * prematurely.
+ */
+ struct ulptx_sgl *sgl = (struct ulptx_sgl *)(cpl + 1);
+ struct sge_txq *tq = &txq->q;
+ int last_desc;
+
+ /* If the Work Request header was an exact multiple of our TX
+ * Descriptor length, then it's possible that the starting SGL
+ * pointer lines up exactly with the end of our TX Descriptor
+ * ring. If that's the case, wrap around to the beginning
+ * here ...
+ */
+ if (unlikely((void *)sgl == (void *)tq->stat)) {
+ sgl = (void *)tq->desc;
+ end = (void *)((void *)tq->desc +
+ ((void *)end - (void *)tq->stat));
+ }
+
+ cxgb4_write_sgl(skb, tq, sgl, end, 0, addr);
+ skb_orphan(skb);
+
+ last_desc = tq->pidx + ndesc - 1;
+ if (last_desc >= tq->size)
+ last_desc -= tq->size;
+ tq->sdesc[last_desc].skb = skb;
+ tq->sdesc[last_desc].sgl = sgl;
+ }
+
+ /* Advance our internal TX Queue state, tell the hardware about
+ * the new TX descriptors and return success.
+ */
+ txq_advance(&txq->q, ndesc);
+
+ cxgb4_ring_tx_db(adapter, &txq->q, ndesc);
+ return NETDEV_TX_OK;
+
+out_free:
+ /* An error of some sort happened. Free the TX skb and tell the
+ * OS that we've "dealt" with the packet ...
+ */
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+netdev_tx_t t4_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct port_info *pi = netdev_priv(dev);
+
+ if (unlikely(pi->eth_flags & PRIV_FLAG_PORT_TX_VM))
+ return cxgb4_vf_eth_xmit(skb, dev);
+
+ return cxgb4_eth_xmit(skb, dev);
+}
+
/**
* reclaim_completed_tx_imm - reclaim completed control-queue Tx descs
* @q: the SGE control Tx queue
--
2.1.0
^ permalink raw reply related
* Re: ECMP routing: problematic selection of outgoing interface
From: Andrew Lunn @ 2018-05-16 13:01 UTC (permalink / raw)
To: Hirotaka Yamamoto; +Cc: netdev@vger.kernel.org
In-Reply-To: <TY1PR0301MB10074DC6D1F5CE4F4B5AF7B5A0920@TY1PR0301MB1007.apcprd03.prod.outlook.com>
On Wed, May 16, 2018 at 01:51:36AM +0000, Hirotaka Yamamoto wrote:
> Hi,
>
> Recently I have built a highly-available network using an ECMP
> route connected to two isolated L2 switches as follows.
>
> Router-- ToR switch 1 ---- Linux
> | 192.168.11.1/24 | eth0: 192.168.11.2/24
> | | eth1: 192.168.12.2/24
> +-- ToR switch 2 ------+
> 192.168.12.1/24
>
> The (default) route has been configured with:
>
> $ sudo ip route add default \
> nexthop via 192.168.11.1 \
> nexthop via 192.168.12.1
>
> Then I found that Linux chooses a wrong outgoing device for some
> destination/source address pairs like this:
>
> $ ip route get 12.34.56.78 from 192.168.12.2:
> 12.34.56.78 from 192.168.12.2 via 192.168.11.1 dev eth0 uid 0
> # dev should be "eth1"
>
> As a consequence, programs like SSH or curl do not work for such
> destinations because routers drop packets having strange source
> addresses.
Hi Hirotaka
I assume you add the 192.168.11.1 and 192.168.12.1 to the interfaces
using global scope? Global scope means the IP addresses are valid
everywhere. All routers should know how to route packets to these IP
addresses. So a host is free to pick any of its global scope IP
addresses and use them. The outgoing interface should not matter,
since all routers downstream of it should have routes for the global
scope IP addresses.
It sounds like your router is doing reverse path filtering. It is
checking its routing table for the source address, and throwing the
packets away if they don't come in the interface the route points out
of. If you don't trust your network, this makes sense. It helps to
stop a host spoofing another host, by sending packets with a spoofed
IP address. But you probably want to do reverse path filtering on the
gateway which borders between the networks you do trust and those you
don't.
Andrew
^ permalink raw reply
* Re: [RFC v4 4/5] virtio_ring: add event idx support in packed ring
From: Tiwei Bie @ 2018-05-16 12:58 UTC (permalink / raw)
To: Jason Wang; +Cc: mst, virtualization, linux-kernel, netdev, wexu, jfreimann
In-Reply-To: <64f5085b-4b62-d545-36e0-0951c11b29a8@redhat.com>
On Wed, May 16, 2018 at 08:17:21PM +0800, Jason Wang wrote:
> On 2018年05月16日 16:37, Tiwei Bie wrote:
[...]
> > @@ -1160,15 +1186,27 @@ static void virtqueue_disable_cb_packed(struct virtqueue *_vq)
> > static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)
> > {
> > struct vring_virtqueue *vq = to_vvq(_vq);
> > + u16 wrap_counter;
> > START_USE(vq);
> > /* We optimistically turn back on interrupts, then check if there was
> > * more to do. */
> > + /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
> > + * either clear the flags bit or point the event index at the next
> > + * entry. Always update the event index to keep code simple. */
> > +
> > + wrap_counter = vq->wrap_counter;
> > + if (vq->last_used_idx > vq->next_avail_idx)
>
> Should this be ">=" consider rx refill may try to completely fill the ring?
It seems that there are two cases that last_used_idx
equals to next_avail_idx. The first one is that the
ring is empty. And the second one is that the ring
is full. Although in the first case, most probably,
the driver won't enable the interrupt.
Maybe I really should track the used_wrap_counter
instead of calculating it each time I need it.. I'll
give it a try..
>
> > + wrap_counter ^= 1;
> > +
> > + vq->vring_packed.driver->off_wrap = cpu_to_virtio16(_vq->vdev,
> > + vq->last_used_idx | (wrap_counter << 15));
> > if (vq->event_flags_shadow == VRING_EVENT_F_DISABLE) {
> > virtio_wmb(vq->weak_barriers);
> > - vq->event_flags_shadow = VRING_EVENT_F_ENABLE;
> > + vq->event_flags_shadow = vq->event ? VRING_EVENT_F_DESC :
> > + VRING_EVENT_F_ENABLE;
> > vq->vring_packed.driver->flags = cpu_to_virtio16(_vq->vdev,
> > vq->event_flags_shadow);
> > }
> > @@ -1194,15 +1232,40 @@ static bool virtqueue_poll_packed(struct virtqueue *_vq, unsigned last_used_idx)
> > static bool virtqueue_enable_cb_delayed_packed(struct virtqueue *_vq)
> > {
> > struct vring_virtqueue *vq = to_vvq(_vq);
> > + u16 bufs, used_idx, wrap_counter;
> > START_USE(vq);
> > /* We optimistically turn back on interrupts, then check if there was
> > * more to do. */
> > + /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
> > + * either clear the flags bit or point the event index at the next
> > + * entry. Always update the event index to keep code simple. */
> > +
> > + /* TODO: tune this threshold */
> > + if (vq->next_avail_idx < vq->last_used_idx)
> > + bufs = (vq->vring_packed.num + vq->next_avail_idx -
> > + vq->last_used_idx) * 3 / 4;
> > + else
> > + bufs = (vq->next_avail_idx - vq->last_used_idx) * 3 / 4;
> > +
> > + wrap_counter = vq->wrap_counter;
> > + if (vq->last_used_idx > vq->next_avail_idx)
> > + wrap_counter ^= 1;
> > +
> > + used_idx = vq->last_used_idx + bufs;
> > + if (used_idx >= vq->vring_packed.num) {
> > + used_idx -= vq->vring_packed.num;
> > + wrap_counter ^= 1;
> > + }
>
> Looks correct but maybe it's better to add some comments for such logic.
Make sense.
>
> > +
> > + vq->vring_packed.driver->off_wrap = cpu_to_virtio16(_vq->vdev,
> > + used_idx | (wrap_counter << 15));
> > if (vq->event_flags_shadow == VRING_EVENT_F_DISABLE) {
> > virtio_wmb(vq->weak_barriers);
> > - vq->event_flags_shadow = VRING_EVENT_F_ENABLE;
> > + vq->event_flags_shadow = vq->event ? VRING_EVENT_F_DESC :
> > + VRING_EVENT_F_ENABLE;
> > vq->vring_packed.driver->flags = cpu_to_virtio16(_vq->vdev,
> > vq->event_flags_shadow);
> > }
> > @@ -1869,8 +1932,10 @@ void vring_transport_features(struct virtio_device *vdev)
> > switch (i) {
> > case VIRTIO_RING_F_INDIRECT_DESC:
> > break;
> > +#if 0
> > case VIRTIO_RING_F_EVENT_IDX:
> > break;
> > +#endif
>
> Maybe it's time to remove this #if 0.
Will do it.
Thanks for the review!
Best regards,
Tiwei Bie
^ permalink raw reply
* [PATCH v2 net-next 12/12] net: stmmac: Remove if condition by taking advantage of hwif return code
From: Jose Abreu @ 2018-05-16 12:50 UTC (permalink / raw)
To: netdev
Cc: Jose Abreu, David S. Miller, Joao Pinto, Vitor Soares,
Giuseppe Cavallaro, Alexandre Torgue
In-Reply-To: <cover.1526474646.git.joabreu@synopsys.com>
We can remove the if condition and check if return code is different
than -EINVAL, meaning callback is present.
Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Vitor Soares <soares@synopsys.com>
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 23 ++++++++++-----------
1 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3ae715e..7af40e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3644,6 +3644,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
/* To handle GMAC own interrupts */
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
int status = stmmac_host_irq_status(priv, priv->hw, &priv->xstats);
+ int mtl_status;
if (unlikely(status)) {
/* For LPI we need to save the tx status */
@@ -3653,20 +3654,18 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
priv->tx_path_in_lpi_mode = false;
}
- if (priv->synopsys_id >= DWMAC_CORE_4_00) {
- for (queue = 0; queue < queues_count; queue++) {
- struct stmmac_rx_queue *rx_q =
- &priv->rx_queue[queue];
+ for (queue = 0; queue < queues_count; queue++) {
+ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
- status |= stmmac_host_mtl_irq_status(priv,
- priv->hw, queue);
+ mtl_status = stmmac_host_mtl_irq_status(priv, priv->hw,
+ queue);
+ if (mtl_status != -EINVAL)
+ status |= mtl_status;
- if (status & CORE_IRQ_MTL_RX_OVERFLOW)
- stmmac_set_rx_tail_ptr(priv,
- priv->ioaddr,
- rx_q->rx_tail_addr,
- queue);
- }
+ if (status & CORE_IRQ_MTL_RX_OVERFLOW)
+ stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
+ rx_q->rx_tail_addr,
+ queue);
}
/* PCS link status */
--
1.7.1
^ permalink raw reply related
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