@@ -38,6 +38,11 @@
#define QEDN_IRQ_NAME_LEN 24
#define QEDN_IRQ_NO_FLAGS 0
+/* HW defines */
+
+/* QEDN_MAX_LLH_PORTS will be extended in future */
+#define QEDN_MAX_LLH_PORTS 16
+
/* Destroy connection defines */
#define QEDN_NON_ABORTIVE_TERMINATION 0
#define QEDN_ABORTIVE_TERMINATION 1
@@ -77,6 +82,7 @@
enum qedn_state {
QEDN_STATE_CORE_PROBED = 0,
QEDN_STATE_CORE_OPEN,
+ QEDN_STATE_LLH_PORT_FILTER_SET,
QEDN_STATE_MFW_STATE,
QEDN_STATE_NVMETCP_OPEN,
QEDN_STATE_IRQ_SET,
@@ -108,6 +114,8 @@ struct qedn_ctx {
/* Accessed with atomic bit ops, used with enum qedn_state */
unsigned long state;
+ u8 num_llh_filters;
+ struct list_head llh_filter_list;
u8 local_mac_addr[ETH_ALEN];
u16 mtu;
@@ -174,6 +182,12 @@ enum qedn_conn_state {
CONN_STATE_DESTROY_COMPLETE
};
+struct qedn_llh_filter {
+ struct list_head entry;
+ u16 port;
+ u16 ref_cnt;
+};
+
struct qedn_ctrl {
struct list_head glb_entry;
struct list_head pf_entry;
@@ -253,5 +267,6 @@ int qedn_initialize_endpoint(struct qedn_endpoint *ep, u8 *local_mac_addr,
int qedn_wait_for_conn_est(struct qedn_conn_ctx *conn_ctx);
int qedn_set_con_state(struct qedn_conn_ctx *conn_ctx, enum qedn_conn_state new_state);
void qedn_terminate_connection(struct qedn_conn_ctx *conn_ctx);
+__be16 qedn_get_in_port(struct sockaddr_storage *sa);
#endif /* _QEDN_H_ */
@@ -22,6 +22,81 @@ static struct pci_device_id qedn_pci_tbl[] = {
{0, 0},
};
+__be16 qedn_get_in_port(struct sockaddr_storage *sa)
+{
+ return sa->ss_family == AF_INET
+ ? ((struct sockaddr_in *)sa)->sin_port
+ : ((struct sockaddr_in6 *)sa)->sin6_port;
+}
+
+struct qedn_llh_filter *qedn_add_llh_filter(struct qedn_ctx *qedn, u16 tcp_port)
+{
+ struct qedn_llh_filter *llh_filter = NULL;
+ struct qedn_llh_filter *llh_tmp = NULL;
+ bool new_filter = 1;
+ int rc = 0;
+
+ /* Check if LLH filter already defined */
+ list_for_each_entry_safe(llh_filter, llh_tmp, &qedn->llh_filter_list, entry) {
+ if (llh_filter->port == tcp_port) {
+ new_filter = 0;
+ llh_filter->ref_cnt++;
+ break;
+ }
+ }
+
+ if (new_filter) {
+ if (qedn->num_llh_filters >= QEDN_MAX_LLH_PORTS) {
+ pr_err("PF reached the max target ports limit %u. %u\n",
+ qedn->dev_info.common.abs_pf_id,
+ qedn->num_llh_filters);
+
+ return NULL;
+ }
+
+ rc = qed_ops->add_src_tcp_port_filter(qedn->cdev, tcp_port);
+ if (rc) {
+ pr_err("LLH port configuration failed. port:%u; rc:%u\n", tcp_port, rc);
+
+ return NULL;
+ }
+
+ llh_filter = kzalloc(sizeof(*llh_filter), GFP_KERNEL);
+ if (!llh_filter) {
+ qed_ops->remove_src_tcp_port_filter(qedn->cdev, tcp_port);
+
+ return NULL;
+ }
+
+ llh_filter->port = tcp_port;
+ llh_filter->ref_cnt = 1;
+ ++qedn->num_llh_filters;
+ list_add_tail(&llh_filter->entry, &qedn->llh_filter_list);
+ set_bit(QEDN_STATE_LLH_PORT_FILTER_SET, &qedn->state);
+ }
+
+ return llh_filter;
+}
+
+void qedn_dec_llh_filter(struct qedn_ctx *qedn, struct qedn_llh_filter *llh_filter)
+{
+ if (!llh_filter)
+ return;
+
+ llh_filter->ref_cnt--;
+ if (!llh_filter->ref_cnt) {
+ list_del(&llh_filter->entry);
+
+ /* Remove LLH protocol port filter */
+ qed_ops->remove_src_tcp_port_filter(qedn->cdev, llh_filter->port);
+
+ --qedn->num_llh_filters;
+ kfree(llh_filter);
+ if (!qedn->num_llh_filters)
+ clear_bit(QEDN_STATE_LLH_PORT_FILTER_SET, &qedn->state);
+ }
+}
+
static bool qedn_matches_qede(struct qedn_ctx *qedn, struct pci_dev *qede_pdev)
{
struct pci_dev *qedn_pdev = qedn->pdev;
@@ -88,8 +163,10 @@ qedn_claim_dev(struct nvme_tcp_ofld_dev *dev,
static int qedn_setup_ctrl(struct nvme_tcp_ofld_ctrl *ctrl, bool new)
{
struct nvme_tcp_ofld_dev *dev = ctrl->dev;
+ struct qedn_llh_filter *llh_filter = NULL;
struct qedn_ctrl *qctrl = NULL;
struct qedn_ctx *qedn = NULL;
+ __be16 remote_port;
int rc = 0;
if (new) {
@@ -116,15 +193,42 @@ static int qedn_setup_ctrl(struct nvme_tcp_ofld_ctrl *ctrl, bool new)
qedn = (struct qedn_ctx *)dev->private_data;
qctrl->qedn = qedn;
- /* Placeholder - setup LLH filter */
+ if (qedn->num_llh_filters == 0) {
+ qedn->mtu = dev->ndev->mtu;
+ memcpy(qedn->local_mac_addr, dev->ndev->dev_addr, ETH_ALEN);
+ }
+
+ remote_port = qedn_get_in_port(&ctrl->conn_params.remote_ip_addr);
+ if (new) {
+ llh_filter = qedn_add_llh_filter(qedn, ntohs(remote_port));
+ if (!llh_filter) {
+ rc = -EFAULT;
+ goto err_out;
+ }
+
+ qctrl->llh_filter = llh_filter;
+ set_bit(LLH_FILTER, &qctrl->agg_state);
+ }
return 0;
+
+err_out:
+ flush_workqueue(qctrl->sp_wq);
+ kfree(qctrl);
+
+ return rc;
}
static int qedn_release_ctrl(struct nvme_tcp_ofld_ctrl *ctrl)
{
struct qedn_ctrl *qctrl = (struct qedn_ctrl *)ctrl->private_data;
+ if (test_and_clear_bit(LLH_FILTER, &qctrl->agg_state) &&
+ qctrl->llh_filter) {
+ qedn_dec_llh_filter(qctrl->qedn, qctrl->llh_filter);
+ qctrl->llh_filter = NULL;
+ }
+
if (test_and_clear_bit(QEDN_STATE_SP_WORK_THREAD_SET, &qctrl->agg_state))
flush_workqueue(qctrl->sp_wq);
@@ -405,6 +509,8 @@ static int qedn_setup_irq(struct qedn_ctx *qedn)
static inline void qedn_init_pf_struct(struct qedn_ctx *qedn)
{
+ INIT_LIST_HEAD(&qedn->llh_filter_list);
+ qedn->num_llh_filters = 0;
hash_init(qedn->conn_ctx_hash);
}
@@ -650,6 +756,12 @@ static void __qedn_remove(struct pci_dev *pdev)
return;
}
+ if (test_and_clear_bit(QEDN_STATE_LLH_PORT_FILTER_SET, &qedn->state)) {
+ pr_err("LLH port configuration removal. %d filters still set\n",
+ qedn->num_llh_filters);
+ qed_ops->clear_all_filters(qedn->cdev);
+ }
+
if (test_and_clear_bit(QEDN_STATE_REGISTERED_OFFLOAD_DEV, &qedn->state))
nvme_tcp_ofld_unregister_dev(&qedn->qedn_ofld_dev);