]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/dccp/feat.c
Merge git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[linux-2.6-omap-h63xx.git] / net / dccp / feat.c
index 77ce2f6b0319a8bd1016818d871b0bf9b9086254..933a0ecf8d463b0649fd69f78335533d86950517 100644 (file)
@@ -6,8 +6,6 @@
  *
  *  ASSUMPTIONS
  *  -----------
- *  o Feature negotiation is coordinated with connection setup (as in TCP), wild
- *    changes of parameters of an established connection are not supported.
  *  o All currently known SP features have 1-byte quantities. If in the future
  *    extensions of RFCs 4340..42 define features with item lengths larger than
  *    one byte, a feature-specific extension of the code will be required.
 
 #define DCCP_FEAT_SP_NOAGREE (-123)
 
-static const struct {
-       u8                      feat_num;               /* DCCPF_xxx */
-       enum dccp_feat_type     rxtx;                   /* RX or TX  */
-       enum dccp_feat_type     reconciliation;         /* SP or NN  */
-       u8                      default_value;          /* as in 6.4 */
-/*
- *    Lookup table for location and type of features (from RFC 4340/4342)
- *  +--------------------------+----+-----+----+----+---------+-----------+
- *  | Feature                  | Location | Reconc. | Initial |  Section  |
- *  |                          | RX | TX  | SP | NN |  Value  | Reference |
- *  +--------------------------+----+-----+----+----+---------+-----------+
- *  | DCCPF_CCID               |    |  X  | X  |    |   2     | 10        |
- *  | DCCPF_SHORT_SEQNOS       |    |  X  | X  |    |   0     |  7.6.1    |
- *  | DCCPF_SEQUENCE_WINDOW    |    |  X  |    | X  | 100     |  7.5.2    |
- *  | DCCPF_ECN_INCAPABLE      | X  |     | X  |    |   0     | 12.1      |
- *  | DCCPF_ACK_RATIO          |    |  X  |    | X  |   2     | 11.3      |
- *  | DCCPF_SEND_ACK_VECTOR    | X  |     | X  |    |   0     | 11.5      |
- *  | DCCPF_SEND_NDP_COUNT     |    |  X  | X  |    |   0     |  7.7.2    |
- *  | DCCPF_MIN_CSUM_COVER     | X  |     | X  |    |   0     |  9.2.1    |
- *  | DCCPF_DATA_CHECKSUM      | X  |     | X  |    |   0     |  9.3.1    |
- *  | DCCPF_SEND_LEV_RATE      | X  |     | X  |    |   0     | 4342/8.4  |
- *  +--------------------------+----+-----+----+----+---------+-----------+
- */
-} dccp_feat_table[] = {
-       { DCCPF_CCID,            FEAT_AT_TX, FEAT_SP, 2 },
-       { DCCPF_SHORT_SEQNOS,    FEAT_AT_TX, FEAT_SP, 0 },
-       { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100 },
-       { DCCPF_ECN_INCAPABLE,   FEAT_AT_RX, FEAT_SP, 0 },
-       { DCCPF_ACK_RATIO,       FEAT_AT_TX, FEAT_NN, 2 },
-       { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0 },
-       { DCCPF_SEND_NDP_COUNT,  FEAT_AT_TX, FEAT_SP, 0 },
-       { DCCPF_MIN_CSUM_COVER,  FEAT_AT_RX, FEAT_SP, 0 },
-       { DCCPF_DATA_CHECKSUM,   FEAT_AT_RX, FEAT_SP, 0 },
-       { DCCPF_SEND_LEV_RATE,   FEAT_AT_RX, FEAT_SP, 0 },
-};
-#define DCCP_FEAT_SUPPORTED_MAX                ARRAY_SIZE(dccp_feat_table)
-
-/**
- * dccp_feat_index  -  Hash function to map feature number into array position
- * Returns consecutive array index or -1 if the feature is not understood.
- */
-static int dccp_feat_index(u8 feat_num)
-{
-       /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */
-       if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM)
-               return feat_num - 1;
-
-       /*
-        * Other features: add cases for new feature types here after adding
-        * them to the above table.
-        */
-       switch (feat_num) {
-       case DCCPF_SEND_LEV_RATE:
-                       return DCCP_FEAT_SUPPORTED_MAX - 1;
-       }
-       return -1;
-}
-
-static u8 dccp_feat_type(u8 feat_num)
-{
-       int idx = dccp_feat_index(feat_num);
-
-       if (idx < 0)
-               return FEAT_UNKNOWN;
-       return dccp_feat_table[idx].reconciliation;
-}
-
-static int dccp_feat_default_value(u8 feat_num)
-{
-       int idx = dccp_feat_index(feat_num);
-
-       return idx < 0 ? : dccp_feat_table[idx].default_value;
-}
-
-/* copy constructor, fval must not already contain allocated memory */
-static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len)
-{
-       fval->sp.len = len;
-       if (fval->sp.len > 0) {
-               fval->sp.vec = kmemdup(val, len, gfp_any());
-               if (fval->sp.vec == NULL) {
-                       fval->sp.len = 0;
-                       return -ENOBUFS;
-               }
-       }
-       return 0;
-}
-
-static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val)
-{
-       if (unlikely(val == NULL))
-               return;
-       if (dccp_feat_type(feat_num) == FEAT_SP)
-               kfree(val->sp.vec);
-       memset(val, 0, sizeof(*val));
-}
-
-static struct dccp_feat_entry *
-             dccp_feat_clone_entry(struct dccp_feat_entry const *original)
-{
-       struct dccp_feat_entry *new;
-       u8 type = dccp_feat_type(original->feat_num);
-
-       if (type == FEAT_UNKNOWN)
-               return NULL;
-
-       new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any());
-       if (new == NULL)
-               return NULL;
-
-       if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val,
-                                                     original->val.sp.vec,
-                                                     original->val.sp.len)) {
-               kfree(new);
-               return NULL;
-       }
-       return new;
-}
-
-static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry)
-{
-       if (entry != NULL) {
-               dccp_feat_val_destructor(entry->feat_num, &entry->val);
-               kfree(entry);
-       }
-}
-
-/*
- * List management functions
- *
- * Feature negotiation lists rely on and maintain the following invariants:
- * - each feat_num in the list is known, i.e. we know its type and default value
- * - each feat_num/is_local combination is unique (old entries are overwritten)
- * - SP values are always freshly allocated
- * - list is sorted in increasing order of feature number (faster lookup)
- */
-static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list,
-                                                    u8 feat_num, bool is_local)
-{
-       struct dccp_feat_entry *entry;
-
-       list_for_each_entry(entry, fn_list, node)
-               if (entry->feat_num == feat_num && entry->is_local == is_local)
-                       return entry;
-               else if (entry->feat_num > feat_num)
-                       break;
-       return NULL;
-}
-
-/**
- * dccp_feat_entry_new  -  Central list update routine (called by all others)
- * @head:  list to add to
- * @feat:  feature number
- * @local: whether the local (1) or remote feature with number @feat is meant
- * This is the only constructor and serves to ensure the above invariants.
- */
-static struct dccp_feat_entry *
-             dccp_feat_entry_new(struct list_head *head, u8 feat, bool local)
-{
-       struct dccp_feat_entry *entry;
-
-       list_for_each_entry(entry, head, node)
-               if (entry->feat_num == feat && entry->is_local == local) {
-                       dccp_feat_val_destructor(entry->feat_num, &entry->val);
-                       return entry;
-               } else if (entry->feat_num > feat) {
-                       head = &entry->node;
-                       break;
-               }
-
-       entry = kmalloc(sizeof(*entry), gfp_any());
-       if (entry != NULL) {
-               entry->feat_num = feat;
-               entry->is_local = local;
-               list_add_tail(&entry->node, head);
-       }
-       return entry;
-}
-
-/**
- * dccp_feat_push_change  -  Add/overwrite a Change option in the list
- * @fn_list: feature-negotiation list to update
- * @feat: one of %dccp_feature_numbers
- * @local: whether local (1) or remote (0) @feat_num is meant
- * @needs_mandatory: whether to use Mandatory feature negotiation options
- * @fval: pointer to NN/SP value to be inserted (will be copied)
- */
-static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
-                                u8 mandatory, dccp_feat_val *fval)
-{
-       struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
-
-       if (new == NULL)
-               return -ENOMEM;
-
-       new->feat_num        = feat;
-       new->is_local        = local;
-       new->state           = FEAT_INITIALISING;
-       new->needs_confirm   = 0;
-       new->empty_confirm   = 0;
-       new->val             = *fval;
-       new->needs_mandatory = mandatory;
-
-       return 0;
-}
-
-/**
- * dccp_feat_push_confirm  -  Add a Confirm entry to the FN list
- * @fn_list: feature-negotiation list to add to
- * @feat: one of %dccp_feature_numbers
- * @local: whether local (1) or remote (0) @feat_num is being confirmed
- * @fval: pointer to NN/SP value to be inserted or NULL
- * Returns 0 on success, a Reset code for further processing otherwise.
- */
-static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local,
-                                 dccp_feat_val *fval)
-{
-       struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
-
-       if (new == NULL)
-               return DCCP_RESET_CODE_TOO_BUSY;
-
-       new->feat_num        = feat;
-       new->is_local        = local;
-       new->state           = FEAT_STABLE;     /* transition in 6.6.2 */
-       new->needs_confirm   = 1;
-       new->empty_confirm   = (fval == NULL);
-       new->val.nn          = 0;               /* zeroes the whole structure */
-       if (!new->empty_confirm)
-               new->val     = *fval;
-       new->needs_mandatory = 0;
-
-       return 0;
-}
-
-static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local)
-{
-       return dccp_feat_push_confirm(fn_list, feat, local, NULL);
-}
-
-static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
-{
-       list_del(&entry->node);
-       dccp_feat_entry_destructor(entry);
-}
-
-void dccp_feat_list_purge(struct list_head *fn_list)
-{
-       struct dccp_feat_entry *entry, *next;
-
-       list_for_each_entry_safe(entry, next, fn_list, node)
-               dccp_feat_entry_destructor(entry);
-       INIT_LIST_HEAD(fn_list);
-}
-EXPORT_SYMBOL_GPL(dccp_feat_list_purge);
-
-/* generate @to as full clone of @from - @to must not contain any nodes */
-int dccp_feat_clone_list(struct list_head const *from, struct list_head *to)
-{
-       struct dccp_feat_entry *entry, *new;
-
-       INIT_LIST_HEAD(to);
-       list_for_each_entry(entry, from, node) {
-               new = dccp_feat_clone_entry(entry);
-               if (new == NULL)
-                       goto cloning_failed;
-               list_add_tail(&new->node, to);
-       }
-       return 0;
-
-cloning_failed:
-       dccp_feat_list_purge(to);
-       return -ENOMEM;
-}
-
 int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
                     u8 *val, u8 len, gfp_t gfp)
 {
@@ -654,9 +377,6 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
 {
        int rc;
 
-       /* Ignore Change requests other than during connection setup */
-       if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING)
-               return 0;
        dccp_feat_debug(type, feature, *val);
 
        /* figure out if it's SP or NN feature */
@@ -706,9 +426,6 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
        int found = 0;
        int all_confirmed = 1;
 
-       /* Ignore Confirm options other than during connection setup */
-       if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING)
-               return 0;
        dccp_feat_debug(type, feature, *val);
 
        /* locate our change request */
@@ -743,6 +460,17 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
                        all_confirmed = 0;
        }
 
+       /* fix re-transmit timer */
+       /* XXX gotta make sure that no option negotiation occurs during
+        * connection shutdown.  Consider that the CLOSEREQ is sent and timer is
+        * on.  if all options are confirmed it might kill timer which should
+        * remain alive until close is received.
+        */
+       if (all_confirmed) {
+               dccp_pr_debug("clear feat negotiation timer %p\n", sk);
+               inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
+       }
+
        if (!found)
                dccp_pr_debug("%s(%d, ...) never requested\n",
                              dccp_feat_typename(type), feature);
@@ -911,8 +639,6 @@ const char *dccp_feat_name(const u8 feat)
        if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC)
                return feature_names[DCCPF_RESERVED];
 
-       if (feat ==  DCCPF_SEND_LEV_RATE)
-               return "Send Loss Event Rate";
        if (feat >= DCCPF_MIN_CCID_SPECIFIC)
                return "CCID-specific";