/*
*
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.89 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.121 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
* Frank Pavlic (pavlic@de.ibm.com) and
* Thomas Spatzier <tspat@de.ibm.com>
*
- * $Revision: 1.89 $ $Date: 2004/04/27 16:27:26 $
+ * $Revision: 1.121 $ $Date: 2004/06/11 16:32:15 $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "qeth_mpc.h"
#include "qeth_fs.h"
-#define VERSION_QETH_C "$Revision: 1.89 $"
-static const char *version = "qeth S/390 OSA-Express driver ("
- VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
- QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
+#define VERSION_QETH_C "$Revision: 1.121 $"
+static const char *version = "qeth S/390 OSA-Express driver";
+
/**
* Debug Facility Stuff
*/
static debug_info_t *qeth_dbf_trace = NULL;
static debug_info_t *qeth_dbf_sense = NULL;
static debug_info_t *qeth_dbf_qerr = NULL;
-static char qeth_dbf_text_buf[255];
+
+DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);
/**
* some more definitions and declarations
/* list of our cards */
struct qeth_card_list_struct qeth_card_list;
+/*process list want to be notified*/
+spinlock_t qeth_notify_lock;
+struct list_head qeth_notify_list;
static void qeth_send_control_data_cb(struct qeth_channel *,
struct qeth_cmd_buffer *);
-static atomic_t qeth_hsi_count;
-
/**
* here we go with function implementation
*/
static int
qeth_set_online(struct ccwgroup_device *);
+
+static struct qeth_ipaddr *
+qeth_get_addr_buffer(enum qeth_prot_versions);
+
+static void
+qeth_notify_processes(void)
+{
+ /*notify all registered processes */
+ struct qeth_notify_list_struct *n_entry;
+
+ QETH_DBF_TEXT(trace,3,"procnoti");
+ spin_lock(&qeth_notify_lock);
+ list_for_each_entry(n_entry, &qeth_notify_list, list) {
+ send_sig(n_entry->signum, n_entry->task, 1);
+ }
+ spin_unlock(&qeth_notify_lock);
+
+}
+int
+qeth_notifier_unregister(struct task_struct *p)
+{
+ struct qeth_notify_list_struct *n_entry, *tmp;
+
+ QETH_DBF_TEXT(trace, 2, "notunreg");
+ spin_lock(&qeth_notify_lock);
+ list_for_each_entry_safe(n_entry, tmp, &qeth_notify_list, list) {
+ if (n_entry->task == p) {
+ list_del(&n_entry->list);
+ kfree(n_entry);
+ goto out;
+ }
+ }
+out:
+ spin_unlock(&qeth_notify_lock);
+ return 0;
+}
+int
+qeth_notifier_register(struct task_struct *p, int signum)
+{
+ struct qeth_notify_list_struct *n_entry;
+
+ QETH_DBF_TEXT(trace, 2, "notreg");
+ /*check first if entry already exists*/
+ spin_lock(&qeth_notify_lock);
+ list_for_each_entry(n_entry, &qeth_notify_list, list) {
+ if (n_entry->task == p) {
+ n_entry->signum = signum;
+ spin_unlock(&qeth_notify_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&qeth_notify_lock);
+
+ n_entry = (struct qeth_notify_list_struct *)
+ kmalloc(sizeof(struct qeth_notify_list_struct),GFP_KERNEL);
+ if (!n_entry)
+ return -ENOMEM;
+ n_entry->task = p;
+ n_entry->signum = signum;
+ spin_lock(&qeth_notify_lock);
+ list_add(&n_entry->list,&qeth_notify_list);
+ spin_unlock(&qeth_notify_lock);
+ return 0;
+}
+
+
/**
* free channel command buffers
*/
ccw_device_set_offline(CARD_RDEV(card));
if (recover_flag == CARD_STATE_UP)
card->state = CARD_STATE_RECOVER;
+ qeth_notify_processes();
return 0;
}
card->use_hard_stop = 1;
qeth_set_offline(cgdev);
}
- if (card->info.type == QETH_CARD_TYPE_IQD)
- atomic_dec(&qeth_hsi_count);
/* remove form our internal list */
write_lock_irqsave(&qeth_card_list.rwlock, flags);
list_del(&card->list);
qeth_set_ip_addr_list(struct qeth_card *card)
{
struct list_head failed_todos;
- struct qeth_ipaddr *todo, *addr, *tmp;
+ struct qeth_ipaddr *todo, *addr;
unsigned long flags;
int rc;
INIT_LIST_HEAD(&failed_todos);
-process_todos:
spin_lock_irqsave(&card->ip_lock, flags);
- list_for_each_entry_safe(todo, tmp, &card->ip_tbd_list, entry) {
+ while (!list_empty(&card->ip_tbd_list)) {
+ todo = list_entry(card->ip_tbd_list.next,
+ struct qeth_ipaddr, entry);
list_del_init(&todo->entry);
rc = __qeth_ref_ip_on_card(card, todo, &addr);
if (rc == 0) {
/* new entry to be added to on-card list */
spin_unlock_irqrestore(&card->ip_lock, flags);
rc = qeth_register_addr_entry(card, todo);
- if (!rc){
- spin_lock_irqsave(&card->ip_lock, flags);
+ spin_lock_irqsave(&card->ip_lock, flags);
+ if (!rc)
list_add_tail(&todo->entry, &card->ip_list);
- spin_unlock_irqrestore(&card->ip_lock, flags);
- } else
+ else
list_add_tail(&todo->entry, &failed_todos);
- goto process_todos;
} else if (rc == -1) {
/* on-card entry to be removed */
list_del_init(&addr->entry);
spin_unlock_irqrestore(&card->ip_lock, flags);
rc = qeth_deregister_addr_entry(card, addr);
+ spin_lock_irqsave(&card->ip_lock, flags);
if (!rc) {
kfree(addr);
kfree(todo);
} else {
- spin_lock_irqsave(&card->ip_lock, flags);
list_add_tail(&addr->entry, &card->ip_list);
list_add_tail(&todo->entry, &failed_todos);
- spin_unlock_irqrestore(&card->ip_lock, flags);
}
- goto process_todos;
}
}
spin_unlock_irqrestore(&card->ip_lock, flags);
card->options.checksum_type = QETH_CHECKSUM_DEFAULT;
card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL;
- card->options.enable_takeover = 1;
card->options.fake_broadcast = 0;
card->options.add_hhlen = DEFAULT_ADD_HHLEN;
card->options.fake_ll = 0;
if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
(CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
card->info.type = known_devices[i][4];
- if (card->options.enable_takeover)
- card->info.func_level = known_devices[i][6];
- else
- card->info.func_level = known_devices[i][7];
card->qdio.no_out_queues = known_devices[i][8];
card->info.is_multicast_different = known_devices[i][9];
return 0;
{
struct qeth_reply *reply;
- reply = kmalloc(sizeof(struct qeth_reply), GFP_KERNEL|GFP_ATOMIC);
+ reply = kmalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
if (reply){
memset(reply, 0, sizeof(struct qeth_reply));
atomic_set(&reply->refcnt, 1);
spin_unlock_irqrestore(&card->lock, flags);
keep_reply = 0;
if (reply->callback != NULL) {
- if (cmd)
+ if (cmd) {
+ reply->offset = (__u16)((char*)cmd -
+ (char *)iob->data);
keep_reply = reply->callback(card,
reply,
(unsigned long)cmd);
+ }
else
keep_reply = reply->callback(card,
reply,
(unsigned long)iob);
}
if (cmd)
- reply->rc = cmd->hdr.return_code;
+ reply->rc = (s16) cmd->hdr.return_code;
else if (iob->rc)
reply->rc = iob->rc;
if (keep_reply) {
init_timer(&timer);
timer.function = qeth_cmd_timeout;
timer.data = (unsigned long) reply;
- timer.expires = jiffies + QETH_TIMEOUT;
+ if (IS_IPA(iob->data))
+ timer.expires = jiffies + QETH_IPA_TIMEOUT;
+ else
+ timer.expires = jiffies + QETH_TIMEOUT;
init_waitqueue_head(&reply->wait_q);
spin_lock_irqsave(&card->lock, flags);
list_add_tail(&reply->list, &card->cmd_waiter_list);
(struct qeth_card *,struct qeth_reply*, unsigned long),
void *reply_param)
{
- struct qeth_ipa_cmd *cmd;
int rc;
QETH_DBF_TEXT(trace,4,"sendipa");
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
&card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
while((skb = qeth_get_next_skb(card, buf->buffer, &element,
&offset, &hdr))){
qeth_rebuild_skb(card, skb, hdr);
-
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.inbound_time += qeth_get_micros() -
- card->perf_stats.inbound_start_time;
- card->perf_stats.inbound_cnt++;
-#endif
+ /* is device UP ? */
+ if (!(card->dev->flags & IFF_UP)){
+ dev_kfree_skb_irq(skb);
+ continue;
+ }
skb->dev = card->dev;
rxrc = netif_rx(skb);
card->dev->last_rx = jiffies;
}
static inline void
-qeth_clear_output_buffer(struct qeth_card *card,
+qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
struct qeth_qdio_out_buffer *buf)
{
int i;
struct sk_buff *skb;
- for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+ /* is PCI flag set on buffer? */
+ if (buf->buffer->element[0].flags & 0x40)
+ atomic_dec(&queue->set_pci_flags_count);
+
+ for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i){
buf->buffer->element[i].length = 0;
buf->buffer->element[i].addr = NULL;
buf->buffer->element[i].flags = 0;
}
}
buf->next_element_to_fill = 0;
- buf->state = QETH_QDIO_BUF_EMPTY;
+ atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
}
static inline void
* 'index') un-requeued -> this buffer is the first buffer that
* will be requeued the next time
*/
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_do_qdio_cnt++;
+ card->perf_stats.inbound_do_qdio_start_time = qeth_get_micros();
+#endif
rc = do_QDIO(CARD_DDEV(card),
- QDIO_FLAG_SYNC_INPUT,
+ QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
0, queue->next_buf_to_init, count, NULL);
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_do_qdio_time += qeth_get_micros() -
+ card->perf_stats.inbound_do_qdio_start_time;
+#endif
if (rc){
PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
"return %i (device %s).\n",
card = (struct qeth_card *) card_ptr;
net_dev = card->dev;
#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_cnt++;
card->perf_stats.inbound_start_time = qeth_get_micros();
#endif
if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
qeth_queue_input_buffer(card, index);
}
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
+#endif
}
static inline int
buf->buffer->element[buf->next_element_to_fill - 1].flags |=
SBAL_FLAGS_LAST_ENTRY;
+ if (queue->card->info.type == QETH_CARD_TYPE_IQD)
+ continue;
+
if (!queue->do_pack){
if ((atomic_read(&queue->used_buffers) >=
(QETH_HIGH_WATERMARK_PACK -
}
queue->card->dev->trans_start = jiffies;
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.outbound_do_qdio_cnt++;
+ queue->card->perf_stats.outbound_do_qdio_start_time = qeth_get_micros();
+#endif
if (under_int)
rc = do_QDIO(CARD_DDEV(queue->card),
QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT,
else
rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT,
queue->queue_no, index, count, NULL);
+#ifdef CONFIG_QETH_PERF_STATS
+ queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() -
+ queue->card->perf_stats.outbound_do_qdio_start_time;
+#endif
if (rc){
QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO "
"returned error (%i) on device %s.",
QETH_DBF_TEXT(trace, 2, "flushbuf");
QETH_DBF_TEXT_(trace, 2, " err%d", rc);
queue->card->stats.tx_errors += count;
+ /* ok, since do_QDIO went wrong the buffers have not been given
+ * to the hardware. they still belong to us, so we can clear
+ * them and reuse then, i.e. set back next_buf_to_fill*/
+ for (i = index; i < index + count; ++i) {
+ buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
+ qeth_clear_output_buffer(queue, buf);
+ }
+ queue->next_buf_to_fill = index;
return;
}
+ atomic_add(count, &queue->used_buffers);
#ifdef CONFIG_QETH_PERF_STATS
queue->card->perf_stats.bufs_sent += count;
- queue->card->perf_stats.outbound_cnt++;
#endif
}
queue->do_pack = 0;
/* flush packing buffers */
buffer = &queue->bufs[queue->next_buf_to_fill];
- BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
- if (buffer->next_element_to_fill > 0) {
- buffer->state = QETH_QDIO_BUF_PRIMED;
+ if ((atomic_read(&buffer->state) ==
+ QETH_QDIO_BUF_EMPTY) &&
+ (buffer->next_element_to_fill > 0)) {
+ atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
flush_count++;
- atomic_inc(&queue->used_buffers);
queue->next_buf_to_fill =
(queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q;
qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue, int under_int)
{
struct qeth_qdio_out_buffer *buffer;
+ int index;
- buffer = &queue->bufs[queue->next_buf_to_fill];
- BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
- if (buffer->next_element_to_fill > 0){
+ index = queue->next_buf_to_fill;
+ buffer = &queue->bufs[index];
+ if((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&
+ (buffer->next_element_to_fill > 0)){
/* it's a packing buffer */
- buffer->state = QETH_QDIO_BUF_PRIMED;
- atomic_inc(&queue->used_buffers);
- qeth_flush_buffers(queue, under_int, queue->next_buf_to_fill,
- 1);
+ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
queue->next_buf_to_fill =
(queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q;
+ qeth_flush_buffers(queue, under_int, index, 1);
}
}
}
}
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_handler_cnt++;
+ card->perf_stats.outbound_handler_start_time = qeth_get_micros();
+#endif
for(i = first_element; i < (first_element + count); ++i){
buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
/*we only handle the KICK_IT error by doing a recovery */
qeth_schedule_recovery(card);
return;
}
- /* is PCI flag set on buffer? */
- if (buffer->buffer->element[0].flags & 0x40)
- atomic_dec(&queue->set_pci_flags_count);
-
- qeth_clear_output_buffer(card, buffer);
- atomic_dec(&queue->used_buffers);
+ qeth_clear_output_buffer(queue, buffer);
}
+ atomic_sub(count, &queue->used_buffers);
netif_wake_queue(card->dev);
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_handler_time += qeth_get_micros() -
+ card->perf_stats.outbound_handler_start_time;
+#endif
}
static char*
/* free outbound qdio_qs */
for (i = 0; i < card->qdio.no_out_queues; ++i){
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
- qeth_clear_output_buffer(card, &card->qdio.
- out_qs[i]->bufs[j]);
+ qeth_clear_output_buffer(card->qdio.out_qs[i],
+ &card->qdio.out_qs[i]->bufs[j]);
kfree(card->qdio.out_qs[i]);
}
kfree(card->qdio.out_qs);
for (i = 0; i < card->qdio.no_out_queues; ++i)
if (card->qdio.out_qs[i]){
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
- qeth_clear_output_buffer(card, &card->qdio.
- out_qs[i]->bufs[j]);
+ qeth_clear_output_buffer(card->qdio.out_qs[i],
+ &card->qdio.out_qs[i]->bufs[j]);
}
}
memset(card->qdio.out_qs[i]->qdio_bufs, 0,
QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
- qeth_clear_output_buffer(card, &card->qdio.
- out_qs[i]->bufs[j]);
+ qeth_clear_output_buffer(card->qdio.out_qs[i],
+ &card->qdio.out_qs[i]->bufs[j]);
}
card->qdio.out_qs[i]->card = card;
card->qdio.out_qs[i]->next_buf_to_fill = 0;
return rc;
}
-static void
-qeth_set_device_name(struct qeth_card *card)
-{
- char buf[IF_NAME_LEN];
-
- memset(buf, 0, IF_NAME_LEN);
- if (card->info.type == QETH_CARD_TYPE_IQD) {
- sprintf(buf,"hsi%d", atomic_read(&qeth_hsi_count));
- atomic_inc(&qeth_hsi_count);
- memcpy(card->dev->name,buf,IF_NAME_LEN);
- }
-
-}
-
static struct net_device *
qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
{
}
break;
case QETH_CARD_TYPE_IQD:
+ dev = alloc_netdev(0, "hsi%d", ether_setup);
+ break;
default:
dev = alloc_etherdev(0);
}
card->stats.tx_carrier_errors++;
return -EIO;
}
- if (netif_queue_stopped(dev) ) {
- card->stats.tx_dropped++;
- return -EBUSY;
- }
#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_cnt++;
card->perf_stats.outbound_start_time = qeth_get_micros();
#endif
/*
if (!(rc = qeth_send_packet(card, skb)))
netif_wake_queue(dev);
+#ifdef CONFIG_QETH_PERF_STATS
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
+#endif
return rc;
}
int first_lap = 1;
QETH_DBF_TEXT(trace, 6, "qdfillbf");
-
buffer = buf->buffer;
atomic_inc(&skb->users);
skb_queue_tail(&buf->skb_list, skb);
if (!queue->do_pack) {
QETH_DBF_TEXT(trace, 6, "fillbfnp");
/* set state to PRIMED -> will be flushed */
- buf->state = QETH_QDIO_BUF_PRIMED;
+ atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
} else {
QETH_DBF_TEXT(trace, 6, "fillbfpa");
#ifdef CONFIG_QETH_PERF_STATS
* packed buffer if full -> set state PRIMED
* -> will be flushed
*/
- buf->state = QETH_QDIO_BUF_PRIMED;
+ atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
}
}
return 0;
}
static inline int
-qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_qdio_out_q *queue, int ipv,
- int cast_type)
+qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ int elements_needed)
+{
+ struct qeth_qdio_out_buffer *buffer;
+ int index;
+
+ QETH_DBF_TEXT(trace, 6, "dosndpfa");
+
+ spin_lock(&queue->lock);
+ index = queue->next_buf_to_fill;
+ buffer = &queue->bufs[queue->next_buf_to_fill];
+ /*
+ * check if buffer is empty to make sure that we do not 'overtake'
+ * ourselves and try to fill a buffer that is already primed
+ */
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
+ card->stats.tx_dropped++;
+ spin_unlock(&queue->lock);
+ return -EBUSY;
+ }
+ queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+ QDIO_MAX_BUFFERS_PER_Q;
+ qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
+ qeth_flush_buffers(queue, 0, index, 1);
+ spin_unlock(&queue->lock);
+ return 0;
+}
+
+static inline int
+qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+ struct sk_buff *skb, struct qeth_hdr *hdr,
+ int elements_needed)
{
- struct qeth_hdr *hdr;
struct qeth_qdio_out_buffer *buffer;
- int elements_needed;
int start_index;
int flush_count = 0;
- int rc;
+ int rc = 0;
QETH_DBF_TEXT(trace, 6, "dosndpkt");
- if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
- QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
- return rc;
- }
- qeth_fill_header(card, hdr, skb, ipv, cast_type);
- elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len)
- >> PAGE_SHIFT);
- if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
- PRINT_ERR("qeth_do_send_packet: invalid size of "
- "IP packet. Discarded.");
- return -EINVAL;
- }
-
spin_lock(&queue->lock);
start_index = queue->next_buf_to_fill;
buffer = &queue->bufs[queue->next_buf_to_fill];
- BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
+ /*
+ * check if buffer is empty to make sure that we do not 'overtake'
+ * ourselves and try to fill a buffer that is already primed
+ */
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+ card->stats.tx_dropped++;
+ spin_unlock(&queue->lock);
+ return -EBUSY;
+ }
if (queue->do_pack){
/* does packet fit in current buffer? */
- if((QETH_MAX_BUFFER_ELEMENTS(card) - buffer->next_element_to_fill)
- < elements_needed){
+ if((QETH_MAX_BUFFER_ELEMENTS(card) -
+ buffer->next_element_to_fill) < elements_needed){
/* ... no -> set state PRIMED */
- buffer->state = QETH_QDIO_BUF_PRIMED;
+ atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
flush_count++;
- atomic_inc(&queue->used_buffers);
queue->next_buf_to_fill =
(queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q;
buffer = &queue->bufs[queue->next_buf_to_fill];
+ /* we did a step forward, so check buffer state again */
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+ card->stats.tx_dropped++;
+ qeth_flush_buffers(queue, 0, start_index, 1);
+ spin_unlock(&queue->lock);
+ /* return EBUSY because we sent old packet, not
+ * the current one */
+ return -EBUSY;
+ }
}
}
-
- rc = qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
- if (rc) {
- PRINT_WARN("qeth_do_send_packet: error during "
- "qeth_fill_buffer.");
- card->stats.tx_dropped++;
- } else if (buffer->state == QETH_QDIO_BUF_PRIMED){
+ qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
+ if (atomic_read(&buffer->state) == QETH_QDIO_BUF_PRIMED){
/* next time fill the next buffer */
flush_count++;
- atomic_inc(&queue->used_buffers);
queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q;
}
/* check if we need to switch packing state of this queue */
- if (card->info.type != QETH_CARD_TYPE_IQD)
- flush_count += qeth_switch_packing_state(queue);
+ flush_count += qeth_switch_packing_state(queue);
if (flush_count)
qeth_flush_buffers(queue, 0, start_index, flush_count);
qeth_flush_buffers_on_no_pci(queue, 0);
spin_unlock(&queue->lock);
-
return rc;
}
int ipv;
int cast_type;
struct qeth_qdio_out_q *queue;
+ struct qeth_hdr *hdr;
+ int elements_needed;
int rc;
QETH_DBF_TEXT(trace, 6, "sendpkt");
cast_type = qeth_get_cast_type(card, skb);
queue = card->qdio.out_qs
[qeth_get_priority_queue(card, skb, ipv, cast_type)];
- /* do we have empty buffers? */
- rc = (atomic_read(&queue->used_buffers) >=
- QDIO_MAX_BUFFERS_PER_Q - 1) ? -EBUSY : 0;
- if (rc) {
- card->stats.tx_dropped++;
+
+ if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
return rc;
}
+ qeth_fill_header(card, hdr, skb, ipv, cast_type);
+ elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len)
+ >> PAGE_SHIFT);
+ if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
+ PRINT_ERR("qeth_do_send_packet: invalid size of "
+ "IP packet. Discarded.");
+ return -EINVAL;
+ }
- rc = qeth_do_send_packet(card, skb, queue, ipv, cast_type);
+ if (card->info.type != QETH_CARD_TYPE_IQD)
+ rc = qeth_do_send_packet(card, queue, skb, hdr,
+ elements_needed);
+ else
+ rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
+ elements_needed);
if (!rc){
card->stats.tx_packets++;
card->stats.tx_bytes += skb->len;
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.outbound_time += qeth_get_micros() -
- card->perf_stats.outbound_start_time;
-#endif
}
return rc;
}
if (rc) {
tmp = rc;
PRINT_WARN("Could not set number of ARP entries on %s: "
- "%s (0x%x)\n",
+ "%s (0x%x/%d)\n",
card->info.if_name, qeth_arp_get_error_cause(&rc),
- tmp);
+ tmp, tmp);
}
return rc;
}
+static inline void
+qeth_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo,
+ struct qeth_arp_query_data *qdata,
+ int entry_size, int uentry_size)
+{
+ char *entry_ptr;
+ char *uentry_ptr;
+ int i;
+
+ entry_ptr = (char *)&qdata->data;
+ uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset);
+ for (i = 0; i < qdata->no_entries; ++i){
+ /* strip off 32 bytes "media specific information" */
+ memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32);
+ entry_ptr += entry_size;
+ uentry_ptr += uentry_size;
+ }
+}
+
static int
qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply,
unsigned long data)
{
- struct qeth_ipa_arp_cmd *cmd;
+ struct qeth_ipa_cmd *cmd;
struct qeth_arp_query_data *qdata;
struct qeth_arp_query_info *qinfo;
int entry_size;
+ int uentry_size;
int i;
QETH_DBF_TEXT(trace,4,"arpquecb");
qinfo = (struct qeth_arp_query_info *) reply->param;
- cmd = (struct qeth_ipa_arp_cmd *) data;
- if (cmd->ihdr.return_code) {
- QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->ihdr.return_code);
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code) {
+ QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->hdr.return_code);
return 0;
}
- if (cmd->shdr.return_code) {
- cmd->ihdr.return_code = cmd->shdr.return_code;
- QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->ihdr.return_code);
+ if (cmd->data.setassparms.hdr.return_code) {
+ cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+ QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->hdr.return_code);
return 0;
}
- qdata = &cmd->data.query_arp;
+ qdata = &cmd->data.setassparms.data.query_arp;
switch(qdata->reply_bits){
case 5:
- entry_size = sizeof(struct qeth_arp_qi_entry5);
+ uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5);
+ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+ uentry_size = sizeof(struct qeth_arp_qi_entry5_short);
break;
case 7:
- entry_size = sizeof(struct qeth_arp_qi_entry7);
- break;
+ /* fall through to default */
default:
/* tr is the same as eth -> entry7 */
- entry_size = sizeof(struct qeth_arp_qi_entry7);
+ uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7);
+ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+ uentry_size = sizeof(struct qeth_arp_qi_entry7_short);
break;
}
/* check if there is enough room in userspace */
if ((qinfo->udata_len - qinfo->udata_offset) <
- qdata->no_entries * entry_size){
+ qdata->no_entries * uentry_size){
QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM);
- cmd->ihdr.return_code = -ENOMEM;
+ cmd->hdr.return_code = -ENOMEM;
+ PRINT_WARN("query ARP user space buffer is too small for "
+ "the returned number of ARP entries. "
+ "Aborting query!\n");
goto out_error;
}
- QETH_DBF_TEXT_(trace, 4, "anore%i", cmd->shdr.number_of_replies);
- QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->shdr.seq_no);
+ QETH_DBF_TEXT_(trace, 4, "anore%i",
+ cmd->data.setassparms.hdr.number_of_replies);
+ QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no);
QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries);
- for (i = 0; i < qdata->no_entries; ++i){
+
+ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) {
+ /* strip off "media specific information" */
+ qeth_copy_arp_entries_stripped(qinfo, qdata, entry_size,
+ uentry_size);
+ } else
+ /*copy entries to user buffer*/
memcpy(qinfo->udata + qinfo->udata_offset,
- qdata->data + i*entry_size, entry_size);
- qinfo->no_entries++;
- qinfo->udata_offset += entry_size;
- }
+ (char *)&qdata->data, qdata->no_entries*uentry_size);
+
+ qinfo->no_entries += qdata->no_entries;
+ qinfo->udata_offset += (qdata->no_entries*uentry_size);
/* check if all replies received ... */
- if (cmd->shdr.seq_no < cmd->shdr.number_of_replies)
+ if (cmd->data.setassparms.hdr.seq_no <
+ cmd->data.setassparms.hdr.number_of_replies)
return 1;
memcpy(qinfo->udata, &qinfo->no_entries, 4);
+ /* keep STRIP_ENTRIES flag so the user program can distinguish
+ * stripped entries from normal ones */
+ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+ qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET,&qdata->reply_bits,2);
return 0;
out_error:
return 0;
}
-static struct qeth_cmd_buffer *
-qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
- enum qeth_prot_versions);
-
-struct qeth_cmd_buffer *
-qeth_get_ipa_arp_cmd_buffer(struct qeth_card *card, u16 cmd_code,
- u32 data_len, enum qeth_prot_versions proto)
+static int
+qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ int len, int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *,
+ unsigned long),
+ void *reply_param)
{
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_arp_cmd *cmd;
- u16 s1, s2;
-
- QETH_DBF_TEXT(trace,4,"getarpcm");
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, proto);
+ QETH_DBF_TEXT(trace,4,"sendarp");
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-
- if ((IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + data_len) > 256) {
- /* adjust sizes in IPA_PDU_HEADER */
- s1 = (u32) IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN +
- data_len;
- s2 = (u32) QETH_ARP_CMD_BASE_LEN + data_len;
- memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
- memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
- }
-
- cmd = (struct qeth_ipa_arp_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->shdr.assist_no = IPA_ARP_PROCESSING;
- cmd->shdr.length = 8 + data_len;
- cmd->shdr.command_code = cmd_code;
- cmd->shdr.return_code = 0;
- cmd->shdr.seq_no = 0;
-
- return iob;
+ memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+ &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+ return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+ reply_cb, reply_param);
}
static int
-qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
- char *data, int data_len,
- int (*reply_cb)
- (struct qeth_card *,struct qeth_reply*, unsigned long),
+qeth_send_ipa_snmp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+ int len, int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *,
+ unsigned long),
void *reply_param)
{
- int rc;
+ u16 s1, s2;
- QETH_DBF_TEXT(trace,4,"sendarp");
+ QETH_DBF_TEXT(trace,4,"sendsnmp");
- memcpy(QETH_IPA_ARP_DATA_POS(iob->data), data, data_len);
+ memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
&card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-
- rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE +
- QETH_ARP_CMD_BASE_LEN + data_len, iob,
- reply_cb, reply_param);
- return rc;
+ /* adjust PDU length fields in IPA_PDU_HEADER */
+ s1 = (u32) IPA_PDU_HEADER_SIZE + len;
+ s2 = (u32) len;
+ memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
+ memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
+ return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+ reply_cb, reply_param);
}
+static struct qeth_cmd_buffer *
+qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
+ __u16, __u16, enum qeth_prot_versions);
static int
qeth_arp_query(struct qeth_card *card, char *udata)
{
struct qeth_cmd_buffer *iob;
- struct qeth_arp_query_data *qdata;
struct qeth_arp_query_info qinfo = {0, };
int tmp;
int rc;
*/
if (card->info.guestlan)
return -EOPNOTSUPP;
- if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+ if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
+ IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
"on %s!\n", card->info.if_name);
return -EOPNOTSUPP;
}
- /* get size of userspace mem area */
- if (copy_from_user(&qinfo.udata_len, udata, 4))
+ /* get size of userspace buffer and mask_bits -> 6 bytes */
+ if (copy_from_user(&qinfo, udata, 6))
return -EFAULT;
if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL)))
return -ENOMEM;
memset(qinfo.udata, 0, qinfo.udata_len);
qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
- /* alloc mem area for the actual query */
- if (!(qdata = kmalloc(sizeof(struct qeth_arp_query_data),
- GFP_KERNEL))){
- kfree(qinfo.udata);
- return -ENOMEM;
- }
- memset(qdata, 0, sizeof(struct qeth_arp_query_data));
- /* do not give sizeof(struct qeth_arp_query_data) to next command;
- * this would cause the IPA PDU size to be set to a value of > 256
- * and this is to much for HiperSockets */
- iob = qeth_get_ipa_arp_cmd_buffer(card, IPA_CMD_ASS_ARP_QUERY_INFO,
- 0, QETH_PROT_IPV4);
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_QUERY_INFO,
+ sizeof(int),QETH_PROT_IPV4);
+
rc = qeth_send_ipa_arp_cmd(card, iob,
- (char *) qdata,
- sizeof(struct qeth_arp_query_data),
- qeth_arp_query_cb,
- (void *)&qinfo);
+ QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
+ qeth_arp_query_cb, (void *)&qinfo);
if (rc) {
tmp = rc;
- PRINT_WARN("Error while querying ARP cache on %s: %s (0x%x)\n",
+ PRINT_WARN("Error while querying ARP cache on %s: %s "
+ "(0x%x/%d)\n",
card->info.if_name, qeth_arp_get_error_cause(&rc),
- tmp);
+ tmp, tmp);
copy_to_user(udata, qinfo.udata, 4);
} else {
copy_to_user(udata, qinfo.udata, qinfo.udata_len);
return rc;
}
+/**
+ * SNMP command callback
+ */
static int
-qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
- unsigned long);
+qeth_snmp_command_cb(struct qeth_card *card, struct qeth_reply *reply,
+ unsigned long sdata)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_arp_query_info *qinfo;
+ struct qeth_snmp_cmd *snmp;
+ unsigned char *data;
+ __u16 data_len;
+
+ QETH_DBF_TEXT(trace,3,"snpcmdcb");
+
+ cmd = (struct qeth_ipa_cmd *) sdata;
+ data = (unsigned char *)((char *)cmd - reply->offset);
+ qinfo = (struct qeth_arp_query_info *) reply->param;
+ snmp = &cmd->data.setadapterparms.data.snmp;
+
+ if (cmd->hdr.return_code) {
+ QETH_DBF_TEXT_(trace,4,"scer1%i", cmd->hdr.return_code);
+ return 0;
+ }
+ if (cmd->data.setadapterparms.hdr.return_code) {
+ cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code;
+ QETH_DBF_TEXT_(trace,4,"scer2%i", cmd->hdr.return_code);
+ return 0;
+ }
+ data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(data));
+ if (cmd->data.setadapterparms.hdr.seq_no == 1)
+ data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
+ else
+ data_len -= (__u16)((char*)&snmp->request - (char *)cmd);
+
+ /* check if there is enough room in userspace */
+ if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
+ QETH_DBF_TEXT_(trace, 4, "scer3%i", -ENOMEM);
+ cmd->hdr.return_code = -ENOMEM;
+ return 0;
+ }
+ QETH_DBF_TEXT_(trace, 4, "snore%i",
+ cmd->data.setadapterparms.hdr.used_total);
+ QETH_DBF_TEXT_(trace, 4, "sseqn%i", cmd->data.setadapterparms.hdr.seq_no);
+ /*copy entries to user buffer*/
+ if (cmd->data.setadapterparms.hdr.seq_no == 1) {
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ (char *)snmp,
+ data_len + offsetof(struct qeth_snmp_cmd,data));
+ qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
+ } else {
+ memcpy(qinfo->udata + qinfo->udata_offset,
+ (char *)&snmp->request, data_len);
+ }
+ qinfo->udata_offset += data_len;
+ /* check if all replies received ... */
+ QETH_DBF_TEXT_(trace, 4, "srtot%i",
+ cmd->data.setadapterparms.hdr.used_total);
+ QETH_DBF_TEXT_(trace, 4, "srseq%i",
+ cmd->data.setadapterparms.hdr.seq_no);
+ if (cmd->data.setadapterparms.hdr.seq_no <
+ cmd->data.setadapterparms.hdr.used_total)
+ return 1;
+ return 0;
+}
static struct qeth_cmd_buffer *
-qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
- __u16, __u16, enum qeth_prot_versions);
+qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
+ enum qeth_prot_versions );
+
+static struct qeth_cmd_buffer *
+qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS,
+ QETH_PROT_IPV4);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setadapterparms.hdr.cmdlength = cmdlen;
+ cmd->data.setadapterparms.hdr.command_code = command;
+ cmd->data.setadapterparms.hdr.used_total = 1;
+ cmd->data.setadapterparms.hdr.seq_no = 1;
+
+ return iob;
+}
+
+/**
+ * function to send SNMP commands to OSA-E card
+ */
+static int
+qeth_snmp_command(struct qeth_card *card, char *udata)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_snmp_ureq *ureq;
+ int req_len;
+ struct qeth_arp_query_info qinfo = {0, };
+ int rc = 0;
+
+ QETH_DBF_TEXT(trace,3,"snmpcmd");
+
+ if (card->info.guestlan)
+ return -EOPNOTSUPP;
+ if (!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) {
+ PRINT_WARN("SNMP Query MIBS not supported "
+ "on %s!\n", card->info.if_name);
+ return -EOPNOTSUPP;
+ }
+ /* skip 4 bytes (data_len struct member) to get req_len */
+ if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))
+ return -EFAULT;
+ ureq = kmalloc(req_len, GFP_KERNEL);
+ if (!ureq) {
+ QETH_DBF_TEXT(trace, 2, "snmpnome");
+ return -ENOMEM;
+ }
+ if (copy_from_user(ureq, udata, req_len)){
+ kfree(ureq);
+ return -EFAULT;
+ }
+ qinfo.udata_len = ureq->hdr.data_len;
+ if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))){
+ kfree(ureq);
+ return -ENOMEM;
+ }
+ memset(qinfo.udata, 0, qinfo.udata_len);
+ qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr);
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL,
+ QETH_SNMP_SETADP_CMDLENGTH + req_len);
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
+ rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
+ qeth_snmp_command_cb, (void *)&qinfo);
+ if (rc)
+ PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
+ card->info.if_name, rc);
+ else
+ copy_to_user(udata, qinfo.udata, qinfo.udata_len);
+
+ kfree(ureq);
+ kfree(qinfo.udata);
+ return rc;
+}
+
+static int
+qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
+ unsigned long);
static int
qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *,
tmp = rc;
qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
PRINT_WARN("Could not add ARP entry for address %s on %s: "
- "%s (0x%x)\n",
+ "%s (0x%x/%d)\n",
buf, card->info.if_name,
- qeth_arp_get_error_cause(&rc), tmp);
+ qeth_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
}
memset(buf, 0, 16);
qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
PRINT_WARN("Could not delete ARP entry for address %s on %s: "
- "%s (0x%x)\n",
+ "%s (0x%x/%d)\n",
buf, card->info.if_name,
- qeth_arp_get_error_cause(&rc), tmp);
+ qeth_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
}
* funcs flags); since all zeros is no valueable information,
* we say EOPNOTSUPP for all ARP functions
*/
- if (card->info.guestlan)
+ if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
return -EOPNOTSUPP;
if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
PRINT_WARN("ARP processing not supported "
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
if (rc){
tmp = rc;
- PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x)\n",
+ PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n",
card->info.if_name, qeth_arp_get_error_cause(&rc),
- tmp);
+ tmp, tmp);
}
return rc;
}
return -ENODEV;
switch (cmd){
- case SIOCDEVPRIVATE:
case SIOC_QETH_ARP_SET_NO_ENTRIES:
if (!capable(CAP_NET_ADMIN)){
rc = -EPERM;
}
rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
break;
- case SIOCDEVPRIVATE+1:
case SIOC_QETH_ARP_QUERY_INFO:
if (!capable(CAP_NET_ADMIN)){
rc = -EPERM;
}
rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
break;
- case SIOCDEVPRIVATE+2:
case SIOC_QETH_ARP_ADD_ENTRY:
if (!capable(CAP_NET_ADMIN)){
rc = -EPERM;
else
rc = qeth_arp_add_entry(card, &arp_entry);
break;
- case SIOCDEVPRIVATE+3:
case SIOC_QETH_ARP_REMOVE_ENTRY:
if (!capable(CAP_NET_ADMIN)){
rc = -EPERM;
else
rc = qeth_arp_remove_entry(card, &arp_entry);
break;
- case SIOCDEVPRIVATE+4:
case SIOC_QETH_ARP_FLUSH_CACHE:
if (!capable(CAP_NET_ADMIN)){
rc = -EPERM;
}
rc = qeth_arp_flush_cache(card);
break;
- case SIOCDEVPRIVATE+5:
case SIOC_QETH_ADP_SET_SNMP_CONTROL:
+ rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
break;
- case SIOCDEVPRIVATE+6:
case SIOC_QETH_GET_CARD_TYPE:
+ if ((card->info.type == QETH_CARD_TYPE_OSAE) &&
+ !card->info.guestlan)
+ return 1;
+ return 0;
break;
case SIOCGMIIPHY:
- mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ mii_data = if_mii(rq);
mii_data->phy_id = 0;
break;
case SIOCGMIIREG:
- mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ mii_data = if_mii(rq);
if (mii_data->phy_id != 0)
rc = -EINVAL;
else
rc = -EPERM;
break;
}
- mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+ mii_data = if_mii(rq);
if (mii_data->phy_id != 0)
rc = -EINVAL;
else
default:
rc = -EOPNOTSUPP;
}
+ if (rc)
+ QETH_DBF_TEXT_(trace, 2, "ioce%d", rc);
return rc;
}
qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
struct qeth_card *card;
+ unsigned long flags;
QETH_DBF_TEXT(trace,4,"vlanreg");
card = (struct qeth_card *) dev->priv;
- spin_lock_irq(&card->vlanlock);
+ spin_lock_irqsave(&card->vlanlock, flags);
card->vlangrp = grp;
- spin_unlock_irq(&card->vlanlock);
+ spin_unlock_irqrestore(&card->vlanlock, flags);
+}
+
+static inline void
+qeth_free_vlan_buffer(struct qeth_card *card, struct qeth_qdio_out_buffer *buf,
+ unsigned short vid)
+{
+ int i;
+ struct sk_buff *skb;
+ struct sk_buff_head tmp_list;
+
+ skb_queue_head_init(&tmp_list);
+ for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+ while ((skb = skb_dequeue(&buf->skb_list))){
+ if (vlan_tx_tag_present(skb) &&
+ (vlan_tx_tag_get(skb) == vid)) {
+ atomic_dec(&skb->users);
+ dev_kfree_skb(skb);
+ } else
+ skb_queue_tail(&tmp_list, skb);
+ }
+ }
+ while ((skb = skb_dequeue(&tmp_list)))
+ skb_queue_tail(&buf->skb_list, skb);
+}
+
+static void
+qeth_free_vlan_skbs(struct qeth_card *card, unsigned short vid)
+{
+ int i, j;
+
+ QETH_DBF_TEXT(trace, 4, "frvlskbs");
+ for (i = 0; i < card->qdio.no_out_queues; ++i){
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
+ qeth_free_vlan_buffer(card, &card->qdio.
+ out_qs[i]->bufs[j], vid);
+ }
+}
+
+static void
+qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ struct qeth_ipaddr *addr;
+
+ QETH_DBF_TEXT(trace, 4, "frvaddr4");
+ if (!card->vlangrp)
+ return;
+ in_dev = in_dev_get(card->vlangrp->vlan_devices[vid]);
+ if (!in_dev)
+ return;
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next){
+ addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
+ if (addr){
+ addr->u.a4.addr = ifa->ifa_address;
+ addr->u.a4.mask = ifa->ifa_mask;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ if (!qeth_delete_ip(card, addr))
+ kfree(addr);
+ }
+ }
+ in_dev_put(in_dev);
+}
+
+static void
+qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
+{
+ struct inet6_dev *in6_dev;
+ struct inet6_ifaddr *ifa;
+ struct qeth_ipaddr *addr;
+
+ QETH_DBF_TEXT(trace, 4, "frvaddr6");
+ if (!card->vlangrp)
+ return;
+ in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]);
+ if (!in6_dev)
+ return;
+ for (ifa = in6_dev->addr_list; ifa; ifa = ifa->lst_next){
+ addr = qeth_get_addr_buffer(QETH_PROT_IPV6);
+ if (addr){
+ memcpy(&addr->u.a6.addr, &ifa->addr,
+ sizeof(struct in6_addr));
+ addr->u.a6.pfxlen = ifa->prefix_len;
+ addr->type = QETH_IP_TYPE_NORMAL;
+ if (!qeth_delete_ip(card, addr))
+ kfree(addr);
+ }
+ }
+ in6_dev_put(in6_dev);
}
static void
qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct qeth_card *card;
+ unsigned long flags;
QETH_DBF_TEXT(trace,4,"vlkilvid");
card = (struct qeth_card *) dev->priv;
- spin_lock_irq(&card->vlanlock);
+ /* free all skbs for the vlan device */
+ qeth_free_vlan_skbs(card, vid);
+ spin_lock_irqsave(&card->vlanlock, flags);
+ /* unregister IP addresses of vlan device */
+ qeth_free_vlan_addresses4(card, vid);
+ qeth_free_vlan_addresses6(card, vid);
if (card->vlangrp)
card->vlangrp->vlan_devices[vid] = NULL;
- spin_unlock_irq(&card->vlanlock);
+ spin_unlock_irqrestore(&card->vlanlock, flags);
+ qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
/* delete mc addresses for this vlan dev */
qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD);
schedule_work(&card->kernel_thread_starter);
memset(addr,0,sizeof(struct qeth_ipaddr));
addr->type = QETH_IP_TYPE_NORMAL;
addr->proto = prot;
- addr->is_multicast = 0;
- addr->users = 0;
- addr->set_flags = 0;
- addr->del_flags = 0;
return addr;
}
return 0;
}
+static void
+qeth_init_func_level(struct qeth_card *card)
+{
+ if (card->ipato.enabled) {
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ card->info.func_level =
+ QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT;
+ else
+ card->info.func_level =
+ QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT;
+ } else {
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ card->info.func_level =
+ QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT;
+ else
+ card->info.func_level =
+ QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT;
+ }
+}
+
/**
* hardsetup card, initialize MPC and QDIO stuff
*/
return rc;
}
qeth_init_tokens(card);
+ qeth_init_func_level(card);
rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
if (rc == -ERESTARTSYS) {
QETH_DBF_TEXT(setup, 2, "break2");
QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
goto out;
}
- qeth_set_device_name(card);
card->dev->priv = card;
card->dev->type = qeth_get_arphdr_type(card->info.type,
card->info.link_type);
return rc;
}
-static struct qeth_cmd_buffer *
-qeth_get_adapter_cmd(struct qeth_card *card, __u32 command)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
-
- iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS,
- QETH_PROT_IPV4);
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->data.setadapterparms.cmdlength =
- sizeof(struct qeth_ipacmd_setadpparms);
- cmd->data.setadapterparms.command_code = command;
- cmd->data.setadapterparms.frames_used_total = 1;
- cmd->data.setadapterparms.frame_seq_no = 1;
-
- return iob;
-}
-
static int
qeth_default_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply,
unsigned long data)
cmd = (struct qeth_ipa_cmd *) data;
if (cmd->hdr.return_code == 0)
- cmd->hdr.return_code = cmd->data.setadapterparms.return_code;
+ cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code;
return 0;
}
struct qeth_cmd_buffer *iob;
QETH_DBF_TEXT(trace,3,"queryadp");
- iob = qeth_get_adapter_cmd(card,IPA_SETADP_QUERY_COMMANDS_SUPPORTED);
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED,
+ sizeof(struct qeth_ipacmd_setadpparms));
rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL);
return rc;
}
QETH_DBF_TEXT(trace,4,"chgmac");
- iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS);
+ iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS,
+ sizeof(struct qeth_ipacmd_setadpparms));
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC;
cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN;
QETH_DBF_TEXT(trace,4,"adpmode");
- iob = qeth_get_adapter_cmd(card, command);
+ iob = qeth_get_adapter_cmd(card, command,
+ sizeof(struct qeth_ipacmd_setadpparms));
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
cmd->data.setadapterparms.data.mode = mode;
rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb,
int rc;
QETH_DBF_TEXT(trace,3,"stbrdcst");
+ card->info.broadcast_capable = 0;
if (!qeth_is_supported(card, IPA_FILTERING)) {
PRINT_WARN("Broadcast not supported on %s\n",
card->info.if_name);
- return -EOPNOTSUPP;
+ rc = -EOPNOTSUPP;
+ goto out;
}
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not enable broadcasting "
+ PRINT_WARN("Could not enable broadcasting filtering "
"on %s: 0x%x\n",
card->info.if_name, rc);
- return rc;
+ goto out;
}
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
if (rc) {
PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
card->info.if_name, rc);
- return rc;
+ goto out;
}
+ card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
PRINT_INFO("Broadcast enabled \n");
- card->dev->flags |= IFF_BROADCAST;
- card->info.broadcast_capable = 1;
- return 0;
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
+ IPA_CMD_ASS_ENABLE, 1);
+ if (rc) {
+ PRINT_WARN("Could not set up broadcast echo filtering on "
+ "%s: 0x%x\n", card->info.if_name, rc);
+ goto out;
+ }
+ card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
+out:
+ if (card->info.broadcast_capable)
+ card->dev->flags |= IFF_BROADCAST;
+ else
+ card->dev->flags &= ~IFF_BROADCAST;
+ return rc;
}
static int
qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
{
struct qeth_ipaddr *addr, *tmp;
- int first_run = 1;
unsigned long flags;
QETH_DBF_TEXT(trace,4,"clearip");
list_del(&addr->entry);
kfree(addr);
}
-again:
- if (first_run)
- first_run = 0;
- else
- spin_lock_irqsave(&card->ip_lock, flags);
- list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+ while (!list_empty(&card->ip_list)) {
+ addr = list_entry(card->ip_list.next,
+ struct qeth_ipaddr, entry);
list_del_init(&addr->entry);
- if (clean){
+ if (clean) {
spin_unlock_irqrestore(&card->ip_lock, flags);
qeth_deregister_addr_entry(card, addr);
+ spin_lock_irqsave(&card->ip_lock, flags);
}
- if (!recover || addr->is_multicast)
+ if (!recover || addr->is_multicast) {
kfree(addr);
- else {
- if (clean)
- spin_lock_irqsave(&card->ip_lock, flags);
- list_add_tail(&addr->entry, &card->ip_tbd_list);
- if (clean) {
- spin_unlock_irqrestore(&card->ip_lock, flags);
- goto again;
- }
+ continue;
}
+ list_add_tail(&addr->entry, &card->ip_tbd_list);
}
spin_unlock_irqrestore(&card->ip_lock, flags);
}
qeth_set_allowed_threads(card, 0xffffffff, 0);
if (recover_flag == CARD_STATE_RECOVER)
qeth_start_again(card);
-
+ qeth_notify_processes();
return 0;
out_remove:
card->use_hard_stop = 1;
int rc=0;
qeth_eyecatcher();
- printk(KERN_INFO "qeth: loading %s\n",version);
+ PRINT_INFO("loading %s (%s/%s/%s/%s/%s/%s/%s %s %s)\n",
+ version, VERSION_QETH_C, VERSION_QETH_H,
+ VERSION_QETH_MPC_H, VERSION_QETH_MPC_C,
+ VERSION_QETH_FS_H, VERSION_QETH_PROC_C,
+ VERSION_QETH_SYS_C, QETH_VERSION_IPV6,
+ QETH_VERSION_VLAN);
INIT_LIST_HEAD(&qeth_card_list.list);
+ INIT_LIST_HEAD(&qeth_notify_list);
+ spin_lock_init(&qeth_notify_lock);
rwlock_init(&qeth_card_list.rwlock);
- atomic_set(&qeth_hsi_count, 0);
if (qeth_register_dbf_views())
goto out_err;
if (qeth_sysfs_register())