diff options
Diffstat (limited to 'drivers/scsi/scsi_transport_iscsi.c')
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index a20f1813cb51..7bf0dec46271 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -727,10 +727,11 @@ static void iscsi_session_release(struct device *dev) kfree(session); } -static int iscsi_is_session_dev(const struct device *dev) +int iscsi_is_session_dev(const struct device *dev) { return dev->release == iscsi_session_release; } +EXPORT_SYMBOL_GPL(iscsi_is_session_dev); static int iscsi_iter_session_fn(struct device *dev, void *data) { @@ -2002,6 +2003,96 @@ iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) } static int +iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh) +{ + struct iscsi_uevent *ev = NLMSG_DATA(nlh); + struct Scsi_Host *shost = NULL; + struct iscsi_chap_rec *chap_rec; + struct iscsi_internal *priv; + struct sk_buff *skbchap; + struct nlmsghdr *nlhchap; + struct iscsi_uevent *evchap; + uint32_t chap_buf_size; + int len, err = 0; + char *buf; + + if (!transport->get_chap) + return -EINVAL; + + priv = iscsi_if_transport_lookup(transport); + if (!priv) + return -EINVAL; + + chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec)); + len = NLMSG_SPACE(sizeof(*ev) + chap_buf_size); + + shost = scsi_host_lookup(ev->u.get_chap.host_no); + if (!shost) { + printk(KERN_ERR "%s: failed. Cound not find host no %u\n", + __func__, ev->u.get_chap.host_no); + return -ENODEV; + } + + do { + int actual_size; + + skbchap = alloc_skb(len, GFP_KERNEL); + if (!skbchap) { + printk(KERN_ERR "can not deliver chap: OOM\n"); + err = -ENOMEM; + goto exit_get_chap; + } + + nlhchap = __nlmsg_put(skbchap, 0, 0, 0, + (len - sizeof(*nlhchap)), 0); + evchap = NLMSG_DATA(nlhchap); + memset(evchap, 0, sizeof(*evchap)); + evchap->transport_handle = iscsi_handle(transport); + evchap->type = nlh->nlmsg_type; + evchap->u.get_chap.host_no = ev->u.get_chap.host_no; + evchap->u.get_chap.chap_tbl_idx = ev->u.get_chap.chap_tbl_idx; + evchap->u.get_chap.num_entries = ev->u.get_chap.num_entries; + buf = (char *) ((char *)evchap + sizeof(*evchap)); + memset(buf, 0, chap_buf_size); + + err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx, + &evchap->u.get_chap.num_entries, buf); + + actual_size = NLMSG_SPACE(sizeof(*ev) + chap_buf_size); + skb_trim(skbchap, NLMSG_ALIGN(actual_size)); + nlhchap->nlmsg_len = actual_size; + + err = iscsi_multicast_skb(skbchap, ISCSI_NL_GRP_ISCSID, + GFP_KERNEL); + } while (err < 0 && err != -ECONNREFUSED); + +exit_get_chap: + scsi_host_put(shost); + return err; +} + +static int iscsi_delete_chap(struct iscsi_transport *transport, + struct iscsi_uevent *ev) +{ + struct Scsi_Host *shost; + int err = 0; + + if (!transport->delete_chap) + return -ENOSYS; + + shost = scsi_host_lookup(ev->u.delete_chap.host_no); + if (!shost) { + printk(KERN_ERR "%s could not find host no %u\n", + __func__, ev->u.delete_chap.host_no); + return -ENODEV; + } + + err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx); + scsi_host_put(shost); + return err; +} + +static int iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) { int err = 0; @@ -2149,6 +2240,12 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) case ISCSI_UEVENT_PING: err = iscsi_send_ping(transport, ev); break; + case ISCSI_UEVENT_GET_CHAP: + err = iscsi_get_chap(transport, nlh); + break; + case ISCSI_UEVENT_DELETE_CHAP: + err = iscsi_delete_chap(transport, ev); + break; default: err = -ENOSYS; break; @@ -2198,6 +2295,8 @@ iscsi_if_rx(struct sk_buff *skb) */ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) break; + if (ev->type == ISCSI_UEVENT_GET_CHAP && !err) + break; err = iscsi_if_send_reply(group, nlh->nlmsg_seq, nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH); |