/** * rmnet_unassociate_network_device() - Unassociate network device * @dev: Device to unassociate * * Frees all structures generate for device. Unregisters rx_handler * todo: needs to do some sanity verification first (is device in use, etc...) * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null * - RMNET_CONFIG_INVALID_REQUEST if device is not already associated * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null */ int rmnet_unassociate_network_device(struct net_device *dev) { struct rmnet_phys_ep_conf_s *config; ASSERT_RTNL(); LOGL("%s(%s);", __func__, dev->name); if (!dev) return RMNET_CONFIG_NO_SUCH_DEVICE; if (!_rmnet_is_physical_endpoint_associated(dev)) return RMNET_CONFIG_INVALID_REQUEST; config = (struct rmnet_phys_ep_conf_s *) rcu_dereference(dev->rx_handler_data); if (!config) return RMNET_CONFIG_UNKNOWN_ERROR; kfree(config); netdev_rx_handler_unregister(dev); return RMNET_CONFIG_OK; }
/** * rmnet_config_notify_cb() - Callback for netdevice notifier chain * @nb: Notifier block data * @event: Netdevice notifier event ID * @data: Contains a net device for which we are getting notified * * Return: * - result of NOTIFY_DONE() */ int rmnet_config_notify_cb(struct notifier_block *nb, unsigned long event, void *data) { struct net_device *dev = (struct net_device *)data; if (!dev) BUG(); LOGL("(..., %lu, %s)", event, dev->name); switch (event) { case NETDEV_UNREGISTER_FINAL: case NETDEV_UNREGISTER: trace_rmnet_unregister_cb_entry(dev); if (_rmnet_is_physical_endpoint_associated(dev)) { LOGH("Kernel is trying to unregister %s", dev->name); rmnet_force_unassociate_device(dev); } trace_rmnet_unregister_cb_exit(dev); break; default: trace_rmnet_unregister_cb_unhandled(dev); LOGD("Unhandeled event [%lu]", event); break; } return NOTIFY_DONE; }
/** * _rmnet_get_phys_ep_config() - Get physical ep config for an associated device * @dev: Device to get endpoint configuration from * * Return: * - pointer to configuration if successful * - 0 (null) if device is not associated */ static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config (struct net_device *dev) { if (_rmnet_is_physical_endpoint_associated(dev)) return (struct rmnet_phys_ep_conf_s *) rcu_dereference(dev->rx_handler_data); else return 0; }
/** * rmnet_unset_logical_endpoint_config() - Un-set logical endpoing configuration * on a device * @dev: Device to set endpoint configuration on * @config_id: logical endpoint id on device * * Retrieves the logical_endpoint_config structure and frees the egress device. * Network device must already have association with RmNet Data driver * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range */ int rmnet_unset_logical_endpoint_config(struct net_device *dev, int config_id) { LOGL("(%s, %d);", dev->name, config_id); if (!dev || ((!_rmnet_is_physical_endpoint_associated(dev)) && (!rmnet_vnd_is_vnd(dev)))) { return RMNET_CONFIG_NO_SUCH_DEVICE; } return _rmnet_unset_logical_endpoint_config(dev, config_id); }
/** * rmnet_associate_network_device() - Associate network device * @dev: Device to register with RmNet data * * Typically used on physical network devices. Registers RX handler and private * metadata structures. * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null * - RMNET_CONFIG_INVALID_REQUEST if the device to be associated is a vnd * - RMNET_CONFIG_DEVICE_IN_USE if dev rx_handler is already filled * - RMNET_CONFIG_DEVICE_IN_USE if netdev_rx_handler_register() fails */ int rmnet_associate_network_device(struct net_device *dev) { struct rmnet_phys_ep_conf_s *config; int rc; ASSERT_RTNL(); LOGL("(%s);\n", dev->name); if (!dev) return RMNET_CONFIG_NO_SUCH_DEVICE; if (_rmnet_is_physical_endpoint_associated(dev)) { LOGM("%s is already regestered", dev->name); return RMNET_CONFIG_DEVICE_IN_USE; } if (rmnet_vnd_is_vnd(dev)) { LOGM("%s is a vnd", dev->name); return RMNET_CONFIG_INVALID_REQUEST; } config = (struct rmnet_phys_ep_conf_s *) kmalloc(sizeof(struct rmnet_phys_ep_conf_s), GFP_ATOMIC); if (!config) return RMNET_CONFIG_NOMEM; memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s)); config->dev = dev; spin_lock_init(&config->agg_lock); rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config); if (rc) { LOGM("netdev_rx_handler_register returns %d", rc); kfree(config); return RMNET_CONFIG_DEVICE_IN_USE; } /* Explicitly hold a reference to the device */ dev_hold(dev); trace_rmnet_associate(dev); return RMNET_CONFIG_OK; }
static void _rmnet_netlink_get_network_device_associated (struct rmnet_nl_msg_s *rmnet_header, struct rmnet_nl_msg_s *resp_rmnet) { struct net_device *dev; _RMNET_NETLINK_NULL_CHECKS(); resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE; dev = dev_get_by_name(&init_net, rmnet_header->data); if (!dev) { resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE; return; } resp_rmnet->return_code = _rmnet_is_physical_endpoint_associated(dev); resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA; dev_put(dev); }
/** * rmnet_set_logical_endpoint_config() - Set logical endpoing configuration on a device * @dev: Device to set endpoint configuration on * @config_id: logical endpoint id on device * @rmnet_mode: endpoint mode. Values from: rmnet_config_endpoint_modes_e * @egress_device: device node to forward packet to once done processing in * ingress/egress handlers * * Creates a logical_endpoint_config structure and fills in the information from * function arguments. Calls _rmnet_set_logical_endpoint_config() to finish * configuration. Network device must already have association with RmNet Data * driver * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is null * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is not handled by * RmNet data module * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range */ int rmnet_set_logical_endpoint_config(struct net_device *dev, int config_id, uint8_t rmnet_mode, struct net_device *egress_dev) { struct rmnet_logical_ep_conf_s epconfig; LOGL("%s(%s, %d, %d, %s);", __func__, dev->name, config_id, rmnet_mode, egress_dev->name); if (!egress_dev || ((!_rmnet_is_physical_endpoint_associated(egress_dev)) && (!rmnet_vnd_is_vnd(egress_dev)))) { return RMNET_CONFIG_BAD_EGRESS_DEVICE; } memset(&epconfig, 0, sizeof(struct rmnet_logical_ep_conf_s)); epconfig.refcount = 1; epconfig.rmnet_mode = rmnet_mode; epconfig.egress_dev = egress_dev; return _rmnet_set_logical_endpoint_config(dev, config_id, &epconfig); }
/** * rmnet_associate_network_device() - Associate network device * @dev: Device to register with RmNet data * * Typically used on physical network devices. Registers RX handler and private * metadata structures. * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null * - RMNET_CONFIG_DEVICE_IN_USE if dev rx_handler is already filled * - RMNET_CONFIG_DEVICE_IN_USE if netdev_rx_handler_register() fails */ int rmnet_associate_network_device(struct net_device *dev) { struct rmnet_phys_ep_conf_s *config; int rc; ASSERT_RTNL(); LOGL("%s(%s);", __func__, dev->name); if (!dev) return RMNET_CONFIG_NO_SUCH_DEVICE; if (_rmnet_is_physical_endpoint_associated(dev)) { LOGM("%s(): %s is already regestered\n", __func__, dev->name); return RMNET_CONFIG_DEVICE_IN_USE; } config = (struct rmnet_phys_ep_conf_s *) kmalloc(sizeof(struct rmnet_phys_ep_conf_s), GFP_ATOMIC); if (!config) return RMNET_CONFIG_NOMEM; memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s)); config->dev = dev; spin_lock_init(&config->agg_lock); rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config); if (rc) { LOGM("%s(): netdev_rx_handler_register returns %d\n", __func__, rc); kfree(config); return RMNET_CONFIG_DEVICE_IN_USE; } return RMNET_CONFIG_OK; }
/** * rmnet_unassociate_network_device() - Unassociate network device * @dev: Device to unassociate * * Frees all structures generate for device. Unregisters rx_handler * todo: needs to do some sanity verification first (is device in use, etc...) * * Return: * - RMNET_CONFIG_OK if successful * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null * - RMNET_CONFIG_INVALID_REQUEST if device is not already associated * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null */ int rmnet_unassociate_network_device(struct net_device *dev) { struct rmnet_phys_ep_conf_s *config; int config_id = RMNET_LOCAL_LOGICAL_ENDPOINT; struct rmnet_logical_ep_conf_s *epconfig_l; ASSERT_RTNL(); LOGL("(%s);", dev->name); if (!dev) return RMNET_CONFIG_NO_SUCH_DEVICE; if (!_rmnet_is_physical_endpoint_associated(dev)) return RMNET_CONFIG_INVALID_REQUEST; for (; config_id < RMNET_DATA_MAX_LOGICAL_EP; config_id++) { epconfig_l = _rmnet_get_logical_ep(dev, config_id); if (epconfig_l && epconfig_l->refcount) return RMNET_CONFIG_DEVICE_IN_USE; } config = (struct rmnet_phys_ep_conf_s *) rcu_dereference(dev->rx_handler_data); if (!config) return RMNET_CONFIG_UNKNOWN_ERROR; kfree(config); netdev_rx_handler_unregister(dev); /* Explicitly release the reference from the device */ dev_put(dev); trace_rmnet_unassociate(dev); return RMNET_CONFIG_OK; }
/** * rmnet_force_unassociate_device() - Force a device to unassociate * @dev: Device to unassociate * * Return: * - void */ static void rmnet_force_unassociate_device(struct net_device *dev) { int i, j; struct net_device *vndev; struct rmnet_logical_ep_conf_s *cfg; struct rmnet_free_vnd_work *vnd_work; ASSERT_RTNL(); if (!dev) BUG(); if (!_rmnet_is_physical_endpoint_associated(dev)) { LOGM("%s", "Called on unassociated device, skipping"); return; } trace_rmnet_unregister_cb_clear_vnds(dev); vnd_work = (struct rmnet_free_vnd_work *) kmalloc(sizeof(struct rmnet_free_vnd_work), GFP_KERNEL); if (!vnd_work) { LOGH("%s", "Out of Memory"); return; } INIT_WORK(&vnd_work->work, _rmnet_free_vnd_later); vnd_work->count = 0; /* Check the VNDs for offending mappings */ for (i = 0, j = 0; i < RMNET_DATA_MAX_VND && j < RMNET_DATA_MAX_VND; i++) { vndev = rmnet_vnd_get_by_id(i); if (!vndev) { LOGL("VND %d not in use; skipping", i); continue; } cfg = rmnet_vnd_get_le_config(vndev); if (!cfg) { LOGH("Got NULL config from VND %d", i); BUG(); continue; } if (cfg->refcount && (cfg->egress_dev == dev)) { rmnet_unset_logical_endpoint_config(vndev, RMNET_LOCAL_LOGICAL_ENDPOINT); vnd_work->vnd_id[j] = i; j++; } } if (j > 0) { vnd_work->count = j; schedule_work(&vnd_work->work); } else { kfree(vnd_work); } /* Clear the mappings on the phys ep */ trace_rmnet_unregister_cb_clear_lepcs(dev); rmnet_unset_logical_endpoint_config(dev, RMNET_LOCAL_LOGICAL_ENDPOINT); for (i = 0; i < RMNET_DATA_MAX_LOGICAL_EP; i++) rmnet_unset_logical_endpoint_config(dev, i); rmnet_unassociate_network_device(dev); }